anndata 0.12.8__tar.gz → 0.12.10__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 (218) hide show
  1. {anndata-0.12.8 → anndata-0.12.10}/PKG-INFO +2 -2
  2. anndata-0.12.10/docs/release-notes/0.12.10.md +6 -0
  3. anndata-0.12.10/docs/release-notes/0.12.9.md +6 -0
  4. {anndata-0.12.8 → anndata-0.12.10}/pyproject.toml +3 -1
  5. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/anndata.py +73 -61
  6. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/specs/methods.py +18 -0
  7. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_settings.py +21 -0
  8. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_settings.pyi +2 -0
  9. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_elementwise.py +107 -0
  10. {anndata-0.12.8 → anndata-0.12.10}/tests/test_x.py +27 -7
  11. {anndata-0.12.8 → anndata-0.12.10}/.cirun.yml +0 -0
  12. {anndata-0.12.8 → anndata-0.12.10}/.codecov.yml +0 -0
  13. {anndata-0.12.8 → anndata-0.12.10}/.cruft.json +0 -0
  14. {anndata-0.12.8 → anndata-0.12.10}/.editorconfig +0 -0
  15. {anndata-0.12.8 → anndata-0.12.10}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  16. {anndata-0.12.8 → anndata-0.12.10}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  17. {anndata-0.12.8 → anndata-0.12.10}/.github/ISSUE_TEMPLATE/enhancement-request.yml +0 -0
  18. {anndata-0.12.8 → anndata-0.12.10}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  19. {anndata-0.12.8 → anndata-0.12.10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  20. {anndata-0.12.8 → anndata-0.12.10}/.github/dependabot.yml +0 -0
  21. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/benchmark.yml +0 -0
  22. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/check-pr.yml +0 -0
  23. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/close-stale.yml +0 -0
  24. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/codespell.yml +0 -0
  25. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/label-stale.yml +0 -0
  26. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/publish.yml +0 -0
  27. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/test-cpu.yml +0 -0
  28. {anndata-0.12.8 → anndata-0.12.10}/.github/workflows/test-gpu.yml +0 -0
  29. {anndata-0.12.8 → anndata-0.12.10}/.gitignore +0 -0
  30. {anndata-0.12.8 → anndata-0.12.10}/.gitmodules +0 -0
  31. {anndata-0.12.8 → anndata-0.12.10}/.pre-commit-config.yaml +0 -0
  32. {anndata-0.12.8 → anndata-0.12.10}/.readthedocs.yml +0 -0
  33. {anndata-0.12.8 → anndata-0.12.10}/.taplo.toml +0 -0
  34. {anndata-0.12.8 → anndata-0.12.10}/.vscode/launch.json +0 -0
  35. {anndata-0.12.8 → anndata-0.12.10}/.vscode/settings.json +0 -0
  36. {anndata-0.12.8 → anndata-0.12.10}/LICENSE +0 -0
  37. {anndata-0.12.8 → anndata-0.12.10}/README.md +0 -0
  38. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/README.md +0 -0
  39. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/asv.conf.json +0 -0
  40. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/__init__.py +0 -0
  41. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/anndata.py +0 -0
  42. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/backed_hdf5.py +0 -0
  43. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/dataset2d.py +0 -0
  44. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/readwrite.py +0 -0
  45. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/sparse_dataset.py +0 -0
  46. {anndata-0.12.8 → anndata-0.12.10}/benchmarks/benchmarks/utils.py +0 -0
  47. {anndata-0.12.8 → anndata-0.12.10}/biome.jsonc +0 -0
  48. {anndata-0.12.8 → anndata-0.12.10}/ci/constraints.txt +0 -0
  49. {anndata-0.12.8 → anndata-0.12.10}/ci/min-constraints.txt +0 -0
  50. {anndata-0.12.8 → anndata-0.12.10}/ci/scripts/min-deps.py +0 -0
  51. {anndata-0.12.8 → anndata-0.12.10}/ci/scripts/towncrier_automation.py +0 -0
  52. {anndata-0.12.8 → anndata-0.12.10}/docs/Makefile +0 -0
  53. {anndata-0.12.8 → anndata-0.12.10}/docs/_key_contributors.rst +0 -0
  54. {anndata-0.12.8 → anndata-0.12.10}/docs/_static/img/anndata_schema.svg +0 -0
  55. {anndata-0.12.8 → anndata-0.12.10}/docs/_templates/autosummary/class.rst +0 -0
  56. {anndata-0.12.8 → anndata-0.12.10}/docs/api.md +0 -0
  57. {anndata-0.12.8 → anndata-0.12.10}/docs/benchmark-read-write.ipynb +0 -0
  58. {anndata-0.12.8 → anndata-0.12.10}/docs/benchmarks.md +0 -0
  59. {anndata-0.12.8 → anndata-0.12.10}/docs/concatenation.rst +0 -0
  60. {anndata-0.12.8 → anndata-0.12.10}/docs/conf.py +0 -0
  61. {anndata-0.12.8 → anndata-0.12.10}/docs/contributing.md +0 -0
  62. {anndata-0.12.8 → anndata-0.12.10}/docs/extensions/autosummary_skip_inherited.py +0 -0
  63. {anndata-0.12.8 → anndata-0.12.10}/docs/extensions/no_skip_abc_members.py +0 -0
  64. {anndata-0.12.8 → anndata-0.12.10}/docs/extensions/patch_myst_cite.py +0 -0
  65. {anndata-0.12.8 → anndata-0.12.10}/docs/fileformat-prose.md +0 -0
  66. {anndata-0.12.8 → anndata-0.12.10}/docs/index.md +0 -0
  67. {anndata-0.12.8 → anndata-0.12.10}/docs/interoperability.md +0 -0
  68. {anndata-0.12.8 → anndata-0.12.10}/docs/references.rst +0 -0
  69. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.0.md +0 -0
  70. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.1.md +0 -0
  71. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.2.md +0 -0
  72. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.3.md +0 -0
  73. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.4.md +0 -0
  74. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.5.md +0 -0
  75. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.6.md +0 -0
  76. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.7.md +0 -0
  77. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.8.md +0 -0
  78. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.10.9.md +0 -0
  79. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.11.0.md +0 -0
  80. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.11.1.md +0 -0
  81. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.11.2.md +0 -0
  82. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.11.3.md +0 -0
  83. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.11.4.md +0 -0
  84. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.0.md +0 -0
  85. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.1.md +0 -0
  86. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.2.md +0 -0
  87. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.3.md +0 -0
  88. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.4.md +0 -0
  89. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.5.md +0 -0
  90. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.6.md +0 -0
  91. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.7.md +0 -0
  92. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.12.8.md +0 -0
  93. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.4.0.md +0 -0
  94. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.5.0.md +0 -0
  95. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.6.0.md +0 -0
  96. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.6.x.md +0 -0
  97. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.0.md +0 -0
  98. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.2.md +0 -0
  99. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.3.md +0 -0
  100. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.4.md +0 -0
  101. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.5.md +0 -0
  102. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.6.md +0 -0
  103. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.7.md +0 -0
  104. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.7.8.md +0 -0
  105. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.8.0.md +0 -0
  106. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.9.0.md +0 -0
  107. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.9.1.md +0 -0
  108. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/0.9.2.md +0 -0
  109. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/2172.bug.md +0 -0
  110. {anndata-0.12.8 → anndata-0.12.10}/docs/release-notes/index.md +0 -0
  111. {anndata-0.12.8 → anndata-0.12.10}/docs/tutorials/index.md +0 -0
  112. {anndata-0.12.8 → anndata-0.12.10}/docs/tutorials/zarr-v3.md +0 -0
  113. {anndata-0.12.8 → anndata-0.12.10}/hatch.toml +0 -0
  114. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/__init__.py +0 -0
  115. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/__init__.py +0 -0
  116. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/access.py +0 -0
  117. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/aligned_df.py +0 -0
  118. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/aligned_mapping.py +0 -0
  119. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/extensions.py +0 -0
  120. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/file_backing.py +0 -0
  121. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/index.py +0 -0
  122. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/merge.py +0 -0
  123. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/raw.py +0 -0
  124. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/sparse_dataset.py +0 -0
  125. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/storage.py +0 -0
  126. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/views.py +0 -0
  127. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_core/xarray.py +0 -0
  128. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/__init__.py +0 -0
  129. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/h5ad.py +0 -0
  130. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/read.py +0 -0
  131. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/specs/__init__.py +0 -0
  132. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/specs/lazy_methods.py +0 -0
  133. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/specs/registry.py +0 -0
  134. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/utils.py +0 -0
  135. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/write.py +0 -0
  136. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_io/zarr.py +0 -0
  137. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_types.py +0 -0
  138. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/_warnings.py +0 -0
  139. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/abc.py +0 -0
  140. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/compat/__init__.py +0 -0
  141. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/__init__.py +0 -0
  142. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/_dispatch_io.py +0 -0
  143. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/backed/__init__.py +0 -0
  144. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/backed/_compat.py +0 -0
  145. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/backed/_io.py +0 -0
  146. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/backed/_lazy_arrays.py +0 -0
  147. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/merge.py +0 -0
  148. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/multi_files/__init__.py +0 -0
  149. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/multi_files/_anncollection.py +0 -0
  150. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/pytorch/__init__.py +0 -0
  151. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/experimental/pytorch/_annloader.py +0 -0
  152. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/io.py +0 -0
  153. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/logging.py +0 -0
  154. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/tests/__init__.py +0 -0
  155. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/tests/helpers.py +0 -0
  156. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/types.py +0 -0
  157. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/typing.py +0 -0
  158. {anndata-0.12.8 → anndata-0.12.10}/src/anndata/utils.py +0 -0
  159. {anndata-0.12.8 → anndata-0.12.10}/src/testing/anndata/__init__.py +0 -0
  160. {anndata-0.12.8 → anndata-0.12.10}/src/testing/anndata/_doctest.py +0 -0
  161. {anndata-0.12.8 → anndata-0.12.10}/src/testing/anndata/_pytest.py +0 -0
  162. {anndata-0.12.8 → anndata-0.12.10}/src/testing/anndata/py.typed +0 -0
  163. {anndata-0.12.8 → anndata-0.12.10}/tests/conftest.py +0 -0
  164. {anndata-0.12.8 → anndata-0.12.10}/tests/data/adata-comments.tsv +0 -0
  165. {anndata-0.12.8 → anndata-0.12.10}/tests/data/adata.csv +0 -0
  166. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/readme.md +0 -0
  167. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.11.4/adata.h5ad +0 -0
  168. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.11.4/adata.zarr.zip +0 -0
  169. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.11.4/readme.md +0 -0
  170. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.5.0/adata.h5ad +0 -0
  171. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.5.0/readme.md +0 -0
  172. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.7.0/adata.h5ad +0 -0
  173. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.7.0/adata.zarr.zip +0 -0
  174. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.7.8/adata.h5ad +0 -0
  175. {anndata-0.12.8 → anndata-0.12.10}/tests/data/archives/v0.7.8/adata.zarr.zip +0 -0
  176. {anndata-0.12.8 → anndata-0.12.10}/tests/data/excel.xlsx +0 -0
  177. {anndata-0.12.8 → anndata-0.12.10}/tests/data/umi_tools.tsv.gz +0 -0
  178. {anndata-0.12.8 → anndata-0.12.10}/tests/lazy/__init__.py +0 -0
  179. {anndata-0.12.8 → anndata-0.12.10}/tests/lazy/conftest.py +0 -0
  180. {anndata-0.12.8 → anndata-0.12.10}/tests/lazy/test_concat.py +0 -0
  181. {anndata-0.12.8 → anndata-0.12.10}/tests/lazy/test_read.py +0 -0
  182. {anndata-0.12.8 → anndata-0.12.10}/tests/lazy/test_write.py +0 -0
  183. {anndata-0.12.8 → anndata-0.12.10}/tests/test_anncollection.py +0 -0
  184. {anndata-0.12.8 → anndata-0.12.10}/tests/test_annot.py +0 -0
  185. {anndata-0.12.8 → anndata-0.12.10}/tests/test_awkward.py +0 -0
  186. {anndata-0.12.8 → anndata-0.12.10}/tests/test_backed_dense.py +0 -0
  187. {anndata-0.12.8 → anndata-0.12.10}/tests/test_backed_hdf5.py +0 -0
  188. {anndata-0.12.8 → anndata-0.12.10}/tests/test_backed_sparse.py +0 -0
  189. {anndata-0.12.8 → anndata-0.12.10}/tests/test_base.py +0 -0
  190. {anndata-0.12.8 → anndata-0.12.10}/tests/test_concatenate.py +0 -0
  191. {anndata-0.12.8 → anndata-0.12.10}/tests/test_concatenate_disk.py +0 -0
  192. {anndata-0.12.8 → anndata-0.12.10}/tests/test_dask.py +0 -0
  193. {anndata-0.12.8 → anndata-0.12.10}/tests/test_dask_view_mem.py +0 -0
  194. {anndata-0.12.8 → anndata-0.12.10}/tests/test_deprecations.py +0 -0
  195. {anndata-0.12.8 → anndata-0.12.10}/tests/test_extensions.py +0 -0
  196. {anndata-0.12.8 → anndata-0.12.10}/tests/test_get_vector.py +0 -0
  197. {anndata-0.12.8 → anndata-0.12.10}/tests/test_gpu.py +0 -0
  198. {anndata-0.12.8 → anndata-0.12.10}/tests/test_helpers.py +0 -0
  199. {anndata-0.12.8 → anndata-0.12.10}/tests/test_inplace_subset.py +0 -0
  200. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_backwards_compat.py +0 -0
  201. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_conversion.py +0 -0
  202. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_dispatched.py +0 -0
  203. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_partial.py +0 -0
  204. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_utils.py +0 -0
  205. {anndata-0.12.8 → anndata-0.12.10}/tests/test_io_warnings.py +0 -0
  206. {anndata-0.12.8 → anndata-0.12.10}/tests/test_layers.py +0 -0
  207. {anndata-0.12.8 → anndata-0.12.10}/tests/test_obsmvarm.py +0 -0
  208. {anndata-0.12.8 → anndata-0.12.10}/tests/test_obspvarp.py +0 -0
  209. {anndata-0.12.8 → anndata-0.12.10}/tests/test_raw.py +0 -0
  210. {anndata-0.12.8 → anndata-0.12.10}/tests/test_readwrite.py +0 -0
  211. {anndata-0.12.8 → anndata-0.12.10}/tests/test_repr.py +0 -0
  212. {anndata-0.12.8 → anndata-0.12.10}/tests/test_settings.py +0 -0
  213. {anndata-0.12.8 → anndata-0.12.10}/tests/test_structured_arrays.py +0 -0
  214. {anndata-0.12.8 → anndata-0.12.10}/tests/test_transpose.py +0 -0
  215. {anndata-0.12.8 → anndata-0.12.10}/tests/test_uns.py +0 -0
  216. {anndata-0.12.8 → anndata-0.12.10}/tests/test_utils.py +0 -0
  217. {anndata-0.12.8 → anndata-0.12.10}/tests/test_views.py +0 -0
  218. {anndata-0.12.8 → anndata-0.12.10}/tests/test_xarray.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anndata
3
- Version: 0.12.8
3
+ Version: 0.12.10
4
4
  Summary: Annotated data.
5
5
  Project-URL: Documentation, https://anndata.readthedocs.io/
6
6
  Project-URL: Source, https://github.com/scverse/anndata
@@ -32,7 +32,7 @@ Requires-Dist: natsort
32
32
  Requires-Dist: numpy>=1.26
33
33
  Requires-Dist: packaging>=24.2
34
34
  Requires-Dist: pandas!=2.1.2,<3,>=2.1.0
35
- Requires-Dist: scipy>=1.12
35
+ Requires-Dist: scipy!=1.17.0,>=1.12
36
36
  Requires-Dist: zarr!=3.0.*,>=2.18.7
37
37
  Provides-Extra: cu11
38
38
  Requires-Dist: cupy-cuda11x; extra == 'cu11'
@@ -0,0 +1,6 @@
1
+ (v0.12.10)=
2
+ ### 0.12.10 {small}`2026-02-06`
3
+
4
+ #### Miscellaneous changes
5
+
6
+ - Add {attr}`anndata.settings.copy_on_write_X` to allow for forward-compatibility with future copy-on-write behavior (release `0.13`). Currently, setting {attr}`~anndata.AnnData.X` on a view implicitly updates the object from which the view was created. {user}`ilan-gold` ({pr}`2327`)
@@ -0,0 +1,6 @@
1
+ (v0.12.9)=
2
+ ### 0.12.9 {small}`2026-01-29`
3
+
4
+ #### Performance
5
+
6
+ - Add a `write_csr_csc_indices_with_min_possible_dtype` option to {attr}`anndata.settings` to enable downcasting of the `indices` of csr and csc matrices to a smaller dtype when writing. For example, if your csr matrix only has 30000 columns, then you can write out the `indices` of that matrix as `uint16` instead of `int64`. {user}`ilan-gold` ({pr}`2159`)
@@ -40,7 +40,7 @@ dependencies = [
40
40
  "pandas >=2.1.0, !=2.1.2, <3",
41
41
  "numpy>=1.26",
42
42
  # https://github.com/scverse/anndata/issues/1434
43
- "scipy >=1.12",
43
+ "scipy >=1.12,!=1.17.0",
44
44
  "h5py>=3.8",
45
45
  "natsort",
46
46
  "packaging>=24.2",
@@ -163,6 +163,8 @@ filterwarnings_when_strict = [
163
163
  "default:.*FixedLengthUTF32:zarr.core.dtype.common.UnstableSpecificationWarning",
164
164
  "default:Automatic shard shape inference is experimental",
165
165
  "default:Writing zarr v2:UserWarning",
166
+ # TODO: Remove in conjunction with or before https://github.com/scverse/anndata/pull/1707
167
+ "default:.*will obey copy-on-write semantics:FutureWarning",
166
168
  ]
167
169
  python_files = [ "test_*.py" ]
168
170
  testpaths = [
@@ -597,8 +597,38 @@ class AnnData(metaclass=utils.DeprecationMixinMeta): # noqa: PLW1641
597
597
  # else:
598
598
  # return X
599
599
 
600
+ def _handle_view_X_cow(self, value: XDataType | None):
601
+ if self._is_view:
602
+ if settings.copy_on_write_X:
603
+ msg = "Setting element `.X` of view, initializing view as actual."
604
+ warnings.warn(msg, ImplicitModificationWarning, stacklevel=2)
605
+ new = self._mutated_copy(X=value)
606
+ self._init_as_actual(new)
607
+ return True
608
+ msg = "Setting element `.X` of view of `AnnData` object will obey copy-on-write semantics in the next minor release. "
609
+ "In other words, this subset of your original `AnnData` will be copied-in-place and initialized with the value passed into this setter. "
610
+ "Set `anndata.settings.copy_on_write_X = True` to begin opting in to this behavior."
611
+ warnings.warn(msg, FutureWarning, stacklevel=2)
612
+ return False
613
+
600
614
  @X.setter
601
615
  def X(self, value: XDataType | None): # noqa: PLR0912
616
+ value = (
617
+ coerce_array(value, name="X", allow_array_like=True)
618
+ if value is not None
619
+ else None
620
+ )
621
+ can_set_direct_if_not_none = value is None or (
622
+ np.isscalar(value)
623
+ or (hasattr(value, "shape") and (self.shape == value.shape))
624
+ or (self.n_vars == 1 and self.n_obs == len(value))
625
+ or (self.n_obs == 1 and self.n_vars == len(value))
626
+ )
627
+ if not can_set_direct_if_not_none:
628
+ msg = f"Data matrix has wrong shape {value.shape}, need to be {self.shape}."
629
+ raise ValueError(msg)
630
+ if self._handle_view_X_cow(value):
631
+ return None
602
632
  if value is None:
603
633
  if self.isbacked:
604
634
  msg = "Cannot currently remove data matrix from backed object."
@@ -607,7 +637,6 @@ class AnnData(metaclass=utils.DeprecationMixinMeta): # noqa: PLW1641
607
637
  self._init_as_actual(self.copy())
608
638
  self._X = None
609
639
  return
610
- value = coerce_array(value, name="X", allow_array_like=True)
611
640
 
612
641
  # If indices are both arrays, we need to modify them
613
642
  # so we don’t set values like coordinates
@@ -620,67 +649,51 @@ class AnnData(metaclass=utils.DeprecationMixinMeta): # noqa: PLW1641
620
649
  oidx, vidx = np.ix_(self._oidx, self._vidx)
621
650
  else:
622
651
  oidx, vidx = self._oidx, self._vidx
623
- if (
624
- np.isscalar(value)
625
- or (hasattr(value, "shape") and (self.shape == value.shape))
626
- or (self.n_vars == 1 and self.n_obs == len(value))
627
- or (self.n_obs == 1 and self.n_vars == len(value))
628
- ):
629
- if not np.isscalar(value):
630
- if self.is_view and any(
631
- isinstance(idx, np.ndarray)
632
- and len(np.unique(idx)) != len(idx.ravel())
633
- for idx in [oidx, vidx]
634
- ):
635
- msg = (
636
- "You are attempting to set `X` to a matrix on a view which has non-unique indices. "
637
- "The resulting `adata.X` will likely not equal the value to which you set it. "
638
- "To avoid this potential issue, please make a copy of the data first. "
639
- "In the future, this operation will throw an error."
640
- )
641
- warnings.warn(msg, FutureWarning, stacklevel=1)
642
- if self.shape != value.shape:
643
- # For assigning vector of values to 2d array or matrix
644
- # Not necessary for row of 2d array
645
- value = value.reshape(self.shape)
646
- if self.isbacked:
647
- if self.is_view:
648
- X = self.file["X"]
649
- if isinstance(X, h5py.Group):
650
- X = sparse_dataset(X)
651
- X[oidx, vidx] = value
652
- else:
653
- self._set_backed("X", value)
654
- elif self.is_view:
655
- if sparse.issparse(self._adata_ref._X) and isinstance(
656
- value, np.ndarray
657
- ):
658
- if isinstance(self._adata_ref.X, CSArray):
659
- memory_class = sparse.coo_array
660
- else:
661
- memory_class = sparse.coo_matrix
662
- value = memory_class(value)
663
- elif sparse.issparse(value) and isinstance(
664
- self._adata_ref._X, np.ndarray
665
- ):
666
- warnings.warn(
667
- "Trying to set a dense array with a sparse array on a view."
668
- "Densifying the sparse array."
669
- "This may incur excessive memory usage",
670
- stacklevel=2,
671
- )
672
- value = value.toarray()
673
- warnings.warn(
674
- "Modifying `X` on a view results in data being overridden",
675
- ImplicitModificationWarning,
676
- stacklevel=2,
652
+ if not np.isscalar(value):
653
+ if self.is_view and any(
654
+ isinstance(idx, np.ndarray) and len(np.unique(idx)) != len(idx.ravel())
655
+ for idx in [oidx, vidx]
656
+ ):
657
+ msg = (
658
+ "You are attempting to set `X` to a matrix on a view which has non-unique indices. "
659
+ "The resulting `adata.X` will likely not equal the value to which you set it. "
660
+ "To avoid this potential issue, please make a copy of the data first. "
661
+ "In the future, this operation will throw an error."
677
662
  )
678
- self._adata_ref._X[oidx, vidx] = value
663
+ warnings.warn(msg, FutureWarning, stacklevel=2)
664
+ if self.shape != value.shape:
665
+ # For assigning vector of values to 2d array or matrix
666
+ # Not necessary for row of 2d array
667
+ value = value.reshape(self.shape)
668
+ if self.isbacked:
669
+ if self.is_view:
670
+ X = self.file["X"]
671
+ if isinstance(X, h5py.Group):
672
+ msg = "Cannot write to views of sparse backed files"
673
+ raise NotImplementedError(msg)
674
+ X[oidx, vidx] = value
679
675
  else:
680
- self._X = value
676
+ self._set_backed("X", value)
677
+ elif self.is_view:
678
+ if sparse.issparse(self._adata_ref._X) and isinstance(value, np.ndarray):
679
+ if isinstance(self._adata_ref.X, CSArray):
680
+ memory_class = sparse.coo_array
681
+ else:
682
+ memory_class = sparse.coo_matrix
683
+ value = memory_class(value)
684
+ elif sparse.issparse(value) and isinstance(self._adata_ref._X, np.ndarray):
685
+ msg = (
686
+ "Trying to set a dense array with a sparse array on a view. "
687
+ "Densifying the sparse array. "
688
+ "This may incur excessive memory usage"
689
+ )
690
+ warnings.warn(msg, UserWarning, stacklevel=2)
691
+ value = value.toarray()
692
+ msg = "Modifying `X` on a view results in data being overridden"
693
+ warnings.warn(msg, ImplicitModificationWarning, stacklevel=2)
694
+ self._adata_ref._X[oidx, vidx] = value
681
695
  else:
682
- msg = f"Data matrix has wrong shape {value.shape}, need to be {self.shape}."
683
- raise ValueError(msg)
696
+ self._X = value
684
697
 
685
698
  @X.deleter
686
699
  def X(self):
@@ -1492,8 +1505,7 @@ class AnnData(metaclass=utils.DeprecationMixinMeta): # noqa: PLW1641
1492
1505
  return self._mutated_copy(
1493
1506
  X=_subset(self._adata_ref.X, (self._oidx, self._vidx)).copy()
1494
1507
  )
1495
- else:
1496
- return self._mutated_copy()
1508
+ return self._mutated_copy()
1497
1509
  else:
1498
1510
  from ..io import read_h5ad, write_h5ad
1499
1511
 
@@ -738,6 +738,24 @@ def write_sparse_compressed(
738
738
  for attr_name in ["data", "indices", "indptr"]:
739
739
  attr = getattr(value, attr_name)
740
740
  dtype = indptr_dtype if attr_name == "indptr" else attr.dtype
741
+ if (
742
+ attr_name == "indices"
743
+ and settings.write_csr_csc_indices_with_min_possible_dtype
744
+ ):
745
+ # np.min_scalar_type can return things like np.ulonglong which zarr doesn't understand
746
+ # and I find this clearer as to what the result type is i.e., unsigned or signed.
747
+ # For example `np.iinfo(np.uint16).max + 1` could be either `uint32` or `int32`,
748
+ # and there's nothing in numpy's docs disallowing this output to change.
749
+ if (minor_axis_size := value.shape[value.format == "csr"]) <= np.iinfo(
750
+ np.uint8
751
+ ).max:
752
+ dtype = np.dtype("uint8")
753
+ elif minor_axis_size <= np.iinfo(np.uint16).max:
754
+ dtype = np.dtype("uint16")
755
+ elif minor_axis_size <= np.iinfo(np.uint32).max:
756
+ dtype = np.dtype("uint32")
757
+ elif minor_axis_size <= np.iinfo(np.uint64).max:
758
+ dtype = np.dtype("uint64")
741
759
  if isinstance(f, H5Group) or is_zarr_v2():
742
760
  g.create_dataset(
743
761
  attr_name, data=attr, shape=attr.shape, dtype=dtype, **dataset_kwargs
@@ -503,6 +503,14 @@ settings.register(
503
503
  get_from_env=check_and_get_bool,
504
504
  )
505
505
 
506
+ settings.register(
507
+ "write_csr_csc_indices_with_min_possible_dtype",
508
+ default_value=False,
509
+ description="Write a csr or csc matrix with the minimum possible data type for `indices`, always unsigned integer.",
510
+ validate=validate_bool,
511
+ get_from_env=check_and_get_bool,
512
+ )
513
+
506
514
  settings.register(
507
515
  "auto_shard_zarr_v3",
508
516
  default_value=False,
@@ -512,5 +520,18 @@ settings.register(
512
520
  )
513
521
 
514
522
 
523
+ settings.register(
524
+ "copy_on_write_X",
525
+ default_value=False,
526
+ description=(
527
+ "Whether to copy-on-write X. "
528
+ "Currently `my_adata_view[subset].X = value` will write back to the original AnnData object at the `subset` location. "
529
+ "`X` is the only element where this behavior is implemented though."
530
+ ),
531
+ validate=validate_bool,
532
+ get_from_env=check_and_get_bool,
533
+ )
534
+
535
+
515
536
  ##################################################################################
516
537
  ##################################################################################
@@ -42,10 +42,12 @@ class _AnnDataSettingsManager(SettingsManager):
42
42
  remove_unused_categories: bool = True
43
43
  check_uniqueness: bool = True
44
44
  allow_write_nullable_strings: bool = False
45
+ copy_on_write_X: bool = False
45
46
  zarr_write_format: Literal[2, 3] = 2
46
47
  use_sparse_array_on_read: bool = False
47
48
  min_rows_for_chunked_h5_copy: int = 1000
48
49
  disallow_forward_slash_in_h5ad: bool = False
50
+ write_csr_csc_indices_with_min_possible_dtype: bool = False
49
51
  auto_shard_zarr_v3: bool = False
50
52
 
51
53
  settings: _AnnDataSettingsManager
@@ -455,6 +455,113 @@ def test_write_indptr_dtype_override(store, sparse_format):
455
455
  np.testing.assert_array_equal(store["X/indptr"][...], X.indptr)
456
456
 
457
457
 
458
+ @pytest.mark.parametrize(
459
+ ("num_minor_axis", "expected_dtype"),
460
+ [
461
+ pytest.param(1, np.dtype("uint8"), id="one_col-expected_uint8_on_disk"),
462
+ pytest.param(
463
+ np.iinfo(np.uint8).max,
464
+ np.dtype("uint8"),
465
+ id="max_np.uint8-matching_dtype_on_disk",
466
+ ),
467
+ pytest.param(
468
+ np.iinfo(np.int8).max,
469
+ np.dtype("uint8"),
470
+ id="max_np.int8-uint8_on_disk",
471
+ ),
472
+ pytest.param(
473
+ np.iinfo(np.uint16).max,
474
+ np.dtype("uint16"),
475
+ id="max_np.uint16-matching_dtype_on_disk",
476
+ ),
477
+ pytest.param(
478
+ np.iinfo(np.int16).max,
479
+ np.dtype("uint16"),
480
+ id="max_np.int16-uint16_on_disk",
481
+ ),
482
+ pytest.param(
483
+ np.iinfo(np.uint32).max,
484
+ np.dtype("uint32"),
485
+ id="max_np.uint32-matching_dtype_on_disk",
486
+ ),
487
+ pytest.param(
488
+ np.iinfo(np.int32).max,
489
+ np.dtype("uint32"),
490
+ id="max_np.int32-uint32_on_disk",
491
+ ),
492
+ pytest.param(
493
+ np.iinfo(np.uint8).max + 1,
494
+ np.dtype("uint16"),
495
+ id="max_np.uint8_plus_one_cols-expected_uint16_on_disk",
496
+ ),
497
+ pytest.param(
498
+ np.iinfo(np.uint16).max + 1,
499
+ np.dtype("uint32"),
500
+ id="max_np.uint16_plus_one_cols-expected_uint32_on_disk",
501
+ ),
502
+ pytest.param(
503
+ np.iinfo(np.uint32).max + 1,
504
+ np.dtype("uint64"),
505
+ id="max_np.uint32_plus_one_cols-expected_uint64_on_disk",
506
+ ),
507
+ pytest.param(
508
+ np.iinfo(np.int64).max + 1,
509
+ np.dtype("uint64"),
510
+ id="max_np.int64_plus_one_cols-expected_uint64_on_disk",
511
+ marks=pytest.mark.xfail(
512
+ reason="scipy sparse does not support bigger than max(int64) values in indices and there is no uint128."
513
+ ),
514
+ ),
515
+ pytest.param(
516
+ np.iinfo(np.uint64).max + 1,
517
+ np.dtype("uint64"),
518
+ id="max_np.uint64_plus_one_cols-expected_uint64_on_disk",
519
+ marks=pytest.mark.xfail(
520
+ reason="scipy sparse does not support bigger than max(int64) values in indices and there is no uint128."
521
+ ),
522
+ ),
523
+ ],
524
+ )
525
+ @pytest.mark.parametrize("format", ["csr", "csc"])
526
+ def test_write_indices_min(
527
+ store: H5Group | ZarrGroup,
528
+ num_minor_axis: int,
529
+ expected_dtype: np.dtype,
530
+ format: Literal["csr", "csc"],
531
+ ):
532
+ minor_axis_index = np.array([num_minor_axis - 1])
533
+ major_axis_index = np.array([10])
534
+ row_cols = (
535
+ (minor_axis_index, major_axis_index)
536
+ if format == "csc"
537
+ else (major_axis_index, minor_axis_index)
538
+ )
539
+ shape = (num_minor_axis, 20) if format == "csc" else (20, num_minor_axis)
540
+ X = getattr(sparse, f"{format}_array")(
541
+ (np.array([10]), row_cols),
542
+ shape=shape,
543
+ )
544
+ assert X.nnz == 1
545
+ with ad.settings.override(write_csr_csc_indices_with_min_possible_dtype=True):
546
+ write_elem(store, "X", X)
547
+
548
+ assert store["X/indices"].dtype == expected_dtype
549
+ with ad.settings.override(use_sparse_array_on_read=True):
550
+ result = read_elem(store["X"])
551
+ assert_equal(result.data, X.data)
552
+ assert_equal(result.indices, X.indices)
553
+ assert_equal(result.indptr, X.indptr)
554
+ assert X.format == result.format
555
+ assert result.shape == X.shape
556
+ # != comparison converts to csr, which allocates a lot of memory or errors out with:
557
+ # ValueError: array is too big; `arr.size * arr.dtype.itemsize` is larger than the maximum possible size.
558
+ # Because the old, very large, minor axis is now the major axis and so either it fails to create or the indptr is very big.
559
+ # The above tests should be enough to capture the desired equality checks so this is mostly for being extra sure.
560
+ # See https://github.com/scipy/scipy/issues/23826
561
+ if not (format == "csc" and num_minor_axis > np.iinfo(np.uint16).max + 1):
562
+ assert (result != X).nnz == 0
563
+
564
+
458
565
  def test_io_spec_raw(store):
459
566
  adata = gen_adata((3, 2), **GEN_ADATA_NO_XARRAY_ARGS)
460
567
  adata.raw = adata.copy()
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from contextlib import nullcontext
6
+
5
7
  import numpy as np
6
8
  import pandas as pd
7
9
  import pytest
@@ -50,17 +52,35 @@ def test_repeat_indices_view():
50
52
 
51
53
  @pytest.mark.parametrize("orig_array_type", UNLABELLED_ARRAY_TYPES)
52
54
  @pytest.mark.parametrize("new_array_type", UNLABELLED_ARRAY_TYPES)
53
- def test_setter_view(orig_array_type, new_array_type):
55
+ @pytest.mark.parametrize("copy_on_write_X", [True, False], ids=["CoW", "update"])
56
+ def test_setter_view(orig_array_type, new_array_type, *, copy_on_write_X: bool):
57
+ ad.settings.copy_on_write_X = copy_on_write_X
54
58
  adata = gen_adata((10, 10), X_type=orig_array_type)
55
59
  orig_X = adata.X
60
+ expected_X = asarray(orig_X.copy())
56
61
  to_assign = new_array_type(np.ones((9, 9)))
57
- if isinstance(orig_X, np.ndarray) and sparse.issparse(to_assign):
58
- # https://github.com/scverse/anndata/issues/500
59
- pytest.xfail("Cannot set a dense array with a sparse array")
62
+ if not copy_on_write_X:
63
+ expected_X[:9, :9] = asarray(to_assign)
60
64
  view = adata[:9, :9]
61
- view.X = to_assign
62
- np.testing.assert_equal(asarray(view.X), np.ones((9, 9)))
63
- assert isinstance(view.X, type(orig_X))
65
+ with (
66
+ pytest.warns(
67
+ ImplicitModificationWarning if copy_on_write_X else FutureWarning,
68
+ match=r"initializing view as actual"
69
+ if copy_on_write_X
70
+ else r"will obey copy-on-write semantics",
71
+ ),
72
+ pytest.warns(UserWarning, match=r"Trying to set a dense array")
73
+ if sparse.issparse(to_assign)
74
+ and isinstance(orig_X, np.ndarray)
75
+ and not copy_on_write_X
76
+ else nullcontext(),
77
+ ):
78
+ view.X = to_assign
79
+ assert_equal(view.X, to_assign)
80
+ assert isinstance(view.X, type(to_assign) if copy_on_write_X else type(orig_X))
81
+ assert_equal(adata.X, expected_X)
82
+ # If cow, then not a view and if not cow, it is a view
83
+ assert view.is_view != copy_on_write_X
64
84
 
65
85
 
66
86
  ###############################
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes