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.
Files changed (454) hide show
  1. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.gitignore +4 -0
  2. cellpy-2.1.2a4/.issueflows/03-solved-issues/cycle_status_2026-08-09.md +18 -0
  3. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue845_original.md +51 -0
  4. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue845_plan.md +124 -0
  5. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue845_status.md +49 -0
  6. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue846_original.md +36 -0
  7. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue846_plan.md +54 -0
  8. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue846_status.md +13 -0
  9. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue849_original.md +65 -0
  10. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue849_plan.md +46 -0
  11. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue849_status.md +13 -0
  12. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue850_original.md +53 -0
  13. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue850_plan.md +44 -0
  14. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue850_status.md +13 -0
  15. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue851_original.md +52 -0
  16. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue851_plan.md +134 -0
  17. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue851_status.md +97 -0
  18. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue853_original.md +66 -0
  19. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue853_plan.md +42 -0
  20. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue853_status.md +12 -0
  21. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue855_original.md +9 -0
  22. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue855_plan.md +47 -0
  23. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue855_status.md +12 -0
  24. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue862_original.md +45 -0
  25. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue862_plan.md +90 -0
  26. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue862_status.md +28 -0
  27. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue863_original.md +35 -0
  28. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue863_plan.md +103 -0
  29. cellpy-2.1.2a4/.issueflows/03-solved-issues/issue863_status.md +33 -0
  30. cellpy-2.1.2a4/.issueflows/04-designs-and-guides/atomic-file-writes.md +45 -0
  31. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/test-registry.md +24 -0
  32. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/HISTORY.md +9 -0
  33. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/PKG-INFO +1 -1
  34. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/cli_api.py +27 -7
  35. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/__init__.py +4 -0
  36. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/collection.py +2 -2
  37. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/collector.py +8 -0
  38. cellpy-2.1.2a4/cellpy/collect/dva.py +95 -0
  39. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/options.py +10 -1
  40. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/__init__.py +3 -1
  41. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/loader.py +136 -21
  42. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/models.py +46 -1
  43. cellpy-2.1.2a4/cellpy/config/session.py +128 -0
  44. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/ica.py +30 -55
  45. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/mpl.py +13 -4
  46. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/plotly.py +12 -4
  47. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/collected.py +77 -1
  48. cellpy-2.1.2a4/cellpy/readers/cellpy_file/atomic.py +78 -0
  49. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/v9.py +36 -11
  50. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/write.py +4 -2
  51. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellreader.py +248 -43
  52. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/config_declarations.py +8 -8
  53. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/uv.lock +3 -3
  54. cellpy-2.1.2a2/cellpy/config/session.py +0 -92
  55. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.aliases +0 -0
  56. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/commands/build.md +0 -0
  57. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/commands/create-original-issue-file.md +0 -0
  58. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/cellpy-core-migration.mdc +0 -0
  59. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/cellpy-workspace.mdc +0 -0
  60. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/graphify.mdc +0 -0
  61. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/issueflow-rules.mdc +0 -0
  62. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/kiss.mdc +0 -0
  63. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/this-project.mdc +0 -0
  64. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/caveman/SKILL.md +0 -0
  65. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/gh-ci/SKILL.md +0 -0
  66. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/grill-me/SKILL.md +0 -0
  67. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow/SKILL.md +0 -0
  68. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-archive/SKILL.md +0 -0
  69. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-auto/SKILL.md +0 -0
  70. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-build/SKILL.md +0 -0
  71. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-cleanup/SKILL.md +0 -0
  72. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-close/SKILL.md +0 -0
  73. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-comments/SKILL.md +0 -0
  74. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-cycle/SKILL.md +0 -0
  75. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-doctor/SKILL.md +0 -0
  76. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-epic/SKILL.md +0 -0
  77. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-fix/SKILL.md +0 -0
  78. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-graphify/SKILL.md +0 -0
  79. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-history-update/SKILL.md +0 -0
  80. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-init/SKILL.md +0 -0
  81. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-issue/SKILL.md +0 -0
  82. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-pause/SKILL.md +0 -0
  83. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-pick/SKILL.md +0 -0
  84. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-plan/SKILL.md +0 -0
  85. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-review/SKILL.md +0 -0
  86. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-status/SKILL.md +0 -0
  87. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-version-bump/SKILL.md +0 -0
  88. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-yolo/SKILL.md +0 -0
  89. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/issueflow-build/SKILL.md +0 -0
  90. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.dockerignore +0 -0
  91. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.editorconfig +0 -0
  92. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.env_example +0 -0
  93. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.gitattributes +0 -0
  94. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/.gitkeep +0 -0
  95. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/README.md +0 -0
  96. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/migrate_prms_calls.py +0 -0
  97. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/scan_hardcoded_headers.py +0 -0
  98. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/scan_member_usage.py +0 -0
  99. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/01-current-issues/.gitkeep +0 -0
  100. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/02-partly-solved-issues/.gitkeep +0 -0
  101. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/.gitkeep +0 -0
  102. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/2026-07-09_archived_issues.md +0 -0
  103. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/2026-07-31_archived_issues.md +0 -0
  104. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/cycle_status_2026-07-31.md +0 -0
  105. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/cycle_status_2026-08-08.md +0 -0
  106. {cellpy-2.1.2a2/.issueflows/02-partly-solved-issues → cellpy-2.1.2a4/.issueflows/03-solved-issues}/issue459_original.md +0 -0
  107. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue786_original.md +0 -0
  108. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue786_plan.md +0 -0
  109. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue786_status.md +0 -0
  110. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue799_original.md +0 -0
  111. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue799_plan.md +0 -0
  112. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue799_status.md +0 -0
  113. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue800_original.md +0 -0
  114. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue800_plan.md +0 -0
  115. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue800_status.md +0 -0
  116. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue801_original.md +0 -0
  117. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue801_plan.md +0 -0
  118. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue801_status.md +0 -0
  119. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue802_original.md +0 -0
  120. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue802_plan.md +0 -0
  121. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue802_status.md +0 -0
  122. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue804_original.md +0 -0
  123. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue804_plan.md +0 -0
  124. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue804_status.md +0 -0
  125. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue809_original.md +0 -0
  126. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue809_plan.md +0 -0
  127. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue809_status.md +0 -0
  128. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue816_original.md +0 -0
  129. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue816_plan.md +0 -0
  130. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue816_status.md +0 -0
  131. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue817_original.md +0 -0
  132. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue817_plan.md +0 -0
  133. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue817_status.md +0 -0
  134. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue818_original.md +0 -0
  135. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue818_plan.md +0 -0
  136. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue818_status.md +0 -0
  137. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue819_original.md +0 -0
  138. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue819_plan.md +0 -0
  139. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue819_status.md +0 -0
  140. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue820_original.md +0 -0
  141. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue820_plan.md +0 -0
  142. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue820_status.md +0 -0
  143. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue821_original.md +0 -0
  144. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue821_plan.md +0 -0
  145. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue821_status.md +0 -0
  146. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue822_original.md +0 -0
  147. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue822_plan.md +0 -0
  148. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue822_status.md +0 -0
  149. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue825_original.md +0 -0
  150. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue825_plan.md +0 -0
  151. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue825_status.md +0 -0
  152. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue837_original.md +0 -0
  153. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue837_plan.md +0 -0
  154. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue837_status.md +0 -0
  155. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue839_original.md +0 -0
  156. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue839_plan.md +0 -0
  157. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue839_status.md +0 -0
  158. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/.gitkeep +0 -0
  159. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/batch-load-orchestrator.md +0 -0
  160. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/bdf-export.md +0 -0
  161. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/build-and-versioning.md +0 -0
  162. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-v2-architecture.excalidraw +0 -0
  163. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-v2-branching.md +0 -0
  164. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-v2-epic.md +0 -0
  165. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-workspace-repos.md +0 -0
  166. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpycell-di-restructuring.md +0 -0
  167. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/ci-tiers.md +0 -0
  168. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cli-api-echo-binder.md +0 -0
  169. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cli-light-startup.md +0 -0
  170. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/docs-on-master.md +0 -0
  171. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/essential-tests.md +0 -0
  172. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/filters-and-plot-filtering.md +0 -0
  173. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/harmonized-raw-default.md +0 -0
  174. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/marimo-docs.md +0 -0
  175. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/otherpath-upath.md +0 -0
  176. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-backends.md +0 -0
  177. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-batch-summary.md +0 -0
  178. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-collected.md +0 -0
  179. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-cycle-legend.md +0 -0
  180. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-prepare.md +0 -0
  181. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-registry.md +0 -0
  182. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/release-procedure.md +0 -0
  183. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/testing-and-coverage.md +0 -0
  184. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/this-project.md +0 -0
  185. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/v2-cellpycore-pin-gate.md +0 -0
  186. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/05-epics/.gitkeep +0 -0
  187. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/05-epics/epic567_plan.md +0 -0
  188. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/config.toml +0 -0
  189. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.readthedocs.yaml +0 -0
  190. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/AGENTS.md +0 -0
  191. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/AUTHORS.md +0 -0
  192. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/CODE_OF_CONDUCT.md +0 -0
  193. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/CONTRIBUTING.md +0 -0
  194. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/DEPRECATIONS.md +0 -0
  195. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/LICENSE.md +0 -0
  196. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/README.md +0 -0
  197. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_data_structure.md +0 -0
  198. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_folder_structure.md +0 -0
  199. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_packaging_pypi.md +0 -0
  200. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_setup.md +0 -0
  201. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_conda_package.md +0 -0
  202. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_docs.md +0 -0
  203. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_loaders_and_instruments.md +0 -0
  204. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_various.md +0 -0
  205. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/index.md +0 -0
  206. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/01_getting_started_tutorial.md +0 -0
  207. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/02_read_cell_data.md +0 -0
  208. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/03_more_about_get.md +0 -0
  209. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/04_other_interactions.md +0 -0
  210. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/05_configuring.md +0 -0
  211. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/06_pandas.md +0 -0
  212. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/07_data_mining.md +0 -0
  213. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/08_the_cellpy_cmd.md +0 -0
  214. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basics.md +0 -0
  215. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/examples.md +0 -0
  216. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/index.md +0 -0
  217. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/01_arbin.md +0 -0
  218. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/02_maccor.md +0 -0
  219. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/03_PEC.md +0 -0
  220. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/04_Neware.md +0 -0
  221. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/05_biologics.md +0 -0
  222. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/06_custom.md +0 -0
  223. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders.md +0 -0
  224. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/images/templates_jupyterlab_001.png +0 -0
  225. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/images/templates_jupyterlab_002.png +0 -0
  226. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_get_cap.ipynb +0 -0
  227. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_simple_plot.ipynb +0 -0
  228. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_templates.ipynb +0 -0
  229. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_xxx-checkpoint.ipynb +0 -0
  230. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks.md +0 -0
  231. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/tips_and_tricks.md +0 -0
  232. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/batch.md +0 -0
  233. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/collectors.md +0 -0
  234. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/custom_file_loaders.md +0 -0
  235. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/easyplot.md +0 -0
  236. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/figures/tutorials_utils_plotting_fig1.png +0 -0
  237. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/figures/tutorials_utils_plotting_fig2.png +0 -0
  238. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/ica.md +0 -0
  239. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/plotting.md +0 -0
  240. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/templates.md +0 -0
  241. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/tut_ocv_rlx.md +0 -0
  242. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils.md +0 -0
  243. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/formats.md +0 -0
  244. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/index.md +0 -0
  245. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/installation.md +0 -0
  246. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/usage.md +0 -0
  247. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/index.md +0 -0
  248. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/README.md +0 -0
  249. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/__init__.py +0 -0
  250. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/baselines/v1x_ubuntu_py313.json +0 -0
  251. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/check_baseline.py +0 -0
  252. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/conftest.py +0 -0
  253. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/paths.py +0 -0
  254. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/test_performance.py +0 -0
  255. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/__init__.py +0 -0
  256. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/_deprecation.py +0 -0
  257. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/_version.py +0 -0
  258. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/__init__.py +0 -0
  259. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/_dbengine.py +0 -0
  260. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/aggregate.py +0 -0
  261. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/db.py +0 -0
  262. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/facade.py +0 -0
  263. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/journal.py +0 -0
  264. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/layout.py +0 -0
  265. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/outputs.py +0 -0
  266. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/policy.py +0 -0
  267. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/qc.py +0 -0
  268. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/result.py +0 -0
  269. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/runner.py +0 -0
  270. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/store.py +0 -0
  271. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/cli.py +0 -0
  272. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/_summary_ops.py +0 -0
  273. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/cells.py +0 -0
  274. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/curves.py +0 -0
  275. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/ica.py +0 -0
  276. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/summary.py +0 -0
  277. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/credentials.py +0 -0
  278. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/legacy.py +0 -0
  279. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/migrate.py +0 -0
  280. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/reference.py +0 -0
  281. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/sources.py +0 -0
  282. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/types.py +0 -0
  283. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exceptions.py +0 -0
  284. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exporters/__init__.py +0 -0
  285. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exporters/bdf.py +0 -0
  286. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exporters/tabular.py +0 -0
  287. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/filters/__init__.py +0 -0
  288. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/filters/cycles.py +0 -0
  289. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/filters/summary.py +0 -0
  290. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/internals/__init__.py +0 -0
  291. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/internals/connections.py +0 -0
  292. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/internals/otherpath.py +0 -0
  293. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/__init__.py +0 -0
  294. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/__init__.py +0 -0
  295. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_alias_module.py +0 -0
  296. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_importing.py +0 -0
  297. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_module.py +0 -0
  298. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_syncronized.py +0 -0
  299. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_version.py +0 -0
  300. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/py.typed +0 -0
  301. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/LICENSE +0 -0
  302. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/__init__.py +0 -0
  303. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/btsda.py +0 -0
  304. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/cli.py +0 -0
  305. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/dicts.py +0 -0
  306. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/formats.py +0 -0
  307. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/main.py +0 -0
  308. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/nda.py +0 -0
  309. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/ndax.py +0 -0
  310. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/utils.py +0 -0
  311. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/version.py +0 -0
  312. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/log.py +0 -0
  313. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/logging.json +0 -0
  314. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/.cellpy_prms_default.conf +0 -0
  315. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/__init__.py +0 -0
  316. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/cell_schema.py +0 -0
  317. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/internal_settings.py +0 -0
  318. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/legacy/__init__.py +0 -0
  319. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/legacy/update_headers.py +0 -0
  320. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/prmreader.py +0 -0
  321. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/prms.py +0 -0
  322. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/__init__.py +0 -0
  323. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/__init__.py +0 -0
  324. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/base.py +0 -0
  325. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/batch_summary.py +0 -0
  326. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/context.py +0 -0
  327. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/cycle_legend.py +0 -0
  328. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/figures.py +0 -0
  329. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/headers.py +0 -0
  330. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/labels.py +0 -0
  331. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/__init__.py +0 -0
  332. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/curves.py +0 -0
  333. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/ica.py +0 -0
  334. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/raw.py +0 -0
  335. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/steps.py +0 -0
  336. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/summary.py +0 -0
  337. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/registry.py +0 -0
  338. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/spec.py +0 -0
  339. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/theme.py +0 -0
  340. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/__init__.py +0 -0
  341. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/capacity_curves.py +0 -0
  342. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/__init__.py +0 -0
  343. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/dtype.py +0 -0
  344. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/fids.py +0 -0
  345. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/format.py +0 -0
  346. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/keys.py +0 -0
  347. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/legacy_read.py +0 -0
  348. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/meta.py +0 -0
  349. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/meta_archive.py +0 -0
  350. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/read.py +0 -0
  351. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/selectors.py +0 -0
  352. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/translate.py +0 -0
  353. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/data_structures.py +0 -0
  354. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/dbreader.py +0 -0
  355. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/do.py +0 -0
  356. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/filefinder.py +0 -0
  357. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/SQL Table IDs.txt +0 -0
  358. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/__init__.py +0 -0
  359. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/_aux_map.py +0 -0
  360. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_res.py +0 -0
  361. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql.py +0 -0
  362. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_7.py +0 -0
  363. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_config.py +0 -0
  364. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_csv.py +0 -0
  365. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_h5.py +0 -0
  366. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_xlsx.py +0 -0
  367. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/base.py +0 -0
  368. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/batmo_bdf.py +0 -0
  369. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/biologics_mpr.py +0 -0
  370. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/__init__.py +0 -0
  371. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/batmo_bdf_bdf.py +0 -0
  372. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_one.py +0 -0
  373. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_three.py +0 -0
  374. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_two.py +0 -0
  375. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_zero.py +0 -0
  376. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/neware_txt_one.py +0 -0
  377. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/neware_txt_two.py +0 -0
  378. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/neware_txt_zero.py +0 -0
  379. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/contract.py +0 -0
  380. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/custom.py +0 -0
  381. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/declarations.py +0 -0
  382. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/ext_nda_reader.py +0 -0
  383. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/harmonize.py +0 -0
  384. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/hooks.py +0 -0
  385. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/loader_specific_modules/__init__.py +0 -0
  386. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/loader_specific_modules/biologic_file_format.py +0 -0
  387. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/local_instrument.py +0 -0
  388. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/maccor_txt.py +0 -0
  389. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/maccor_txt_native.py +0 -0
  390. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/neware_nda.py +0 -0
  391. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/neware_txt.py +0 -0
  392. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/neware_xlsx.py +0 -0
  393. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/pec_csv.py +0 -0
  394. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/processors/__init__.py +0 -0
  395. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/processors/post_processors.py +0 -0
  396. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/processors/pre_processors.py +0 -0
  397. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/registry.py +0 -0
  398. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/testing.py +0 -0
  399. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/journal_layer.py +0 -0
  400. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/json_dbreader.py +0 -0
  401. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/merger.py +0 -0
  402. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/meta_resolver.py +0 -0
  403. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/native_core.py +0 -0
  404. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/provenance.py +0 -0
  405. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/slicing.py +0 -0
  406. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/sql_dbreader.py +0 -0
  407. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/test_meta.py +0 -0
  408. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/units.py +0 -0
  409. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/__init__.py +0 -0
  410. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/batch.py +0 -0
  411. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/collectors.py +0 -0
  412. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/diagnostics.py +0 -0
  413. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/example_data.py +0 -0
  414. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/helpers.py +0 -0
  415. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/ica.py +0 -0
  416. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/live.py +0 -0
  417. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/ocv_rlx.py +0 -0
  418. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/plotutils.py +0 -0
  419. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/processor.py +0 -0
  420. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/template_registry.py +0 -0
  421. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/backfill_notebook_plotly_pngs.py +0 -0
  422. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/conda-recipes/README.md +0 -0
  423. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/conda-recipes/cellpy/meta.yaml +0 -0
  424. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/conda-recipes/cellpycore/meta.yaml +0 -0
  425. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/make_bad_fixtures.py +0 -0
  426. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/plot_preview_common.py +0 -0
  427. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_curve_plots.py +0 -0
  428. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_ica_plots.py +0 -0
  429. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_plots.py +0 -0
  430. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_summary_plots.py +0 -0
  431. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/regenerate_goldens.py +0 -0
  432. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/render_example_notebooks.py +0 -0
  433. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/snapshot_cli_surface.py +0 -0
  434. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/snapshot_figure_specs.py +0 -0
  435. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/Dockerfile.build-test +0 -0
  436. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/README.md +0 -0
  437. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/compose.yml +0 -0
  438. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/data/hello.txt +0 -0
  439. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/data/nested/sample.txt +0 -0
  440. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/environment.yml +0 -0
  441. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/environment_dev.yml +0 -0
  442. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/github_actions_environment.yml +0 -0
  443. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/noxfile.py +0 -0
  444. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/.github/workflows/draft-pdf.yml +0 -0
  445. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/.gitignore +0 -0
  446. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/Figures/Cellpy-Utils.jpg +0 -0
  447. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/Figures/CellpyCell.jpg +0 -0
  448. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/Figures/CellpyData.jpg +0 -0
  449. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/paper.bib +0 -0
  450. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/paper.md +0 -0
  451. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/pyproject.toml +0 -0
  452. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/scripts/build_test.sh +0 -0
  453. {cellpy-2.1.2a2 → cellpy-2.1.2a4}/scripts/dev_sync.sh +0 -0
  454. {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,13 @@
1
+ # Status: #849
2
+
3
+ - [x] Done
4
+
5
+ ## What's done
6
+
7
+ - Scrub instrument credential keys in `model_dump_for_file`
8
+ - Reject those keys on TOML load; drop+warn on legacy YAML
9
+ - Essential tests + HISTORY
10
+
11
+ ## Remaining work
12
+
13
+ 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,13 @@
1
+ # Status: #850
2
+
3
+ - [x] Done
4
+
5
+ ## What's done
6
+
7
+ - ContextVar override stack + context-local config
8
+ - Thread isolation essential test
9
+ - HISTORY
10
+
11
+ ## Remaining work
12
+
13
+ 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.