anndata 0.12.0rc4__tar.gz → 0.12.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/test-cpu.yml +2 -1
  2. {anndata-0.12.0rc4 → anndata-0.12.1}/.pre-commit-config.yaml +2 -2
  3. {anndata-0.12.0rc4 → anndata-0.12.1}/PKG-INFO +9 -10
  4. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/asv.conf.json +2 -0
  5. anndata-0.12.1/benchmarks/benchmarks/dataset2d.py +61 -0
  6. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/benchmarks/sparse_dataset.py +18 -7
  7. {anndata-0.12.0rc4 → anndata-0.12.1}/biome.jsonc +2 -2
  8. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/conf.py +4 -3
  9. anndata-0.12.1/docs/release-notes/0.12.0.md +51 -0
  10. anndata-0.12.1/docs/release-notes/0.12.1.md +10 -0
  11. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/tutorials/zarr-v3.md +17 -0
  12. {anndata-0.12.0rc4 → anndata-0.12.1}/pyproject.toml +10 -13
  13. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/aligned_df.py +1 -1
  14. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/anndata.py +1 -1
  15. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/merge.py +0 -6
  16. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/sparse_dataset.py +5 -1
  17. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/xarray.py +7 -4
  18. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/h5ad.py +1 -1
  19. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/read.py +5 -1
  20. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/specs/lazy_methods.py +25 -15
  21. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/specs/methods.py +18 -16
  22. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/specs/registry.py +15 -4
  23. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/zarr.py +0 -14
  24. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_settings.py +5 -2
  25. anndata-0.12.1/src/anndata/_settings.pyi +49 -0
  26. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/backed/_lazy_arrays.py +27 -5
  27. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/tests/helpers.py +8 -24
  28. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/utils.py +3 -4
  29. {anndata-0.12.0rc4 → anndata-0.12.1}/src/testing/anndata/_pytest.py +4 -0
  30. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/conftest.py +13 -1
  31. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/lazy/conftest.py +12 -3
  32. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/lazy/test_read.py +33 -3
  33. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_awkward.py +2 -1
  34. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_backed_hdf5.py +8 -5
  35. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_backed_sparse.py +10 -10
  36. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_base.py +3 -1
  37. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_concatenate_disk.py +12 -5
  38. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_deprecations.py +2 -2
  39. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_backwards_compat.py +3 -3
  40. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_dispatched.py +10 -5
  41. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_elementwise.py +85 -15
  42. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_warnings.py +2 -2
  43. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_readwrite.py +20 -1
  44. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_settings.py +19 -0
  45. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_structured_arrays.py +12 -21
  46. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_utils.py +4 -2
  47. anndata-0.12.0rc4/docs/release-notes/0.12.0rc1.md +0 -26
  48. anndata-0.12.0rc4/docs/release-notes/0.12.0rc2.md +0 -14
  49. anndata-0.12.0rc4/docs/release-notes/0.12.0rc3.md +0 -6
  50. anndata-0.12.0rc4/docs/release-notes/0.12.0rc4.md +0 -15
  51. anndata-0.12.0rc4/docs/release-notes/2013.development.md +0 -1
  52. {anndata-0.12.0rc4 → anndata-0.12.1}/.cirun.yml +0 -0
  53. {anndata-0.12.0rc4 → anndata-0.12.1}/.codecov.yml +0 -0
  54. {anndata-0.12.0rc4 → anndata-0.12.1}/.editorconfig +0 -0
  55. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  56. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  57. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/ISSUE_TEMPLATE/enhancement-request.yml +0 -0
  58. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  59. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  60. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/benchmark.yml +0 -0
  61. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/check-pr-milestoned.yml +0 -0
  62. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/close-stale.yml +0 -0
  63. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/codespell.yml +0 -0
  64. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/label-stale.yml +0 -0
  65. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/publish.yml +0 -0
  66. {anndata-0.12.0rc4 → anndata-0.12.1}/.github/workflows/test-gpu.yml +0 -0
  67. {anndata-0.12.0rc4 → anndata-0.12.1}/.gitignore +0 -0
  68. {anndata-0.12.0rc4 → anndata-0.12.1}/.gitmodules +0 -0
  69. {anndata-0.12.0rc4 → anndata-0.12.1}/.prettierignore +0 -0
  70. {anndata-0.12.0rc4 → anndata-0.12.1}/.prettierrc.yaml +0 -0
  71. {anndata-0.12.0rc4 → anndata-0.12.1}/.readthedocs.yml +0 -0
  72. {anndata-0.12.0rc4 → anndata-0.12.1}/.taplo.toml +0 -0
  73. {anndata-0.12.0rc4 → anndata-0.12.1}/.vscode/launch.json +0 -0
  74. {anndata-0.12.0rc4 → anndata-0.12.1}/.vscode/settings.json +0 -0
  75. {anndata-0.12.0rc4 → anndata-0.12.1}/LICENSE +0 -0
  76. {anndata-0.12.0rc4 → anndata-0.12.1}/README.md +0 -0
  77. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/README.md +0 -0
  78. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/benchmarks/__init__.py +0 -0
  79. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/benchmarks/anndata.py +0 -0
  80. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/benchmarks/readwrite.py +0 -0
  81. {anndata-0.12.0rc4 → anndata-0.12.1}/benchmarks/benchmarks/utils.py +0 -0
  82. {anndata-0.12.0rc4 → anndata-0.12.1}/ci/constraints.txt +0 -0
  83. {anndata-0.12.0rc4 → anndata-0.12.1}/ci/scripts/min-deps.py +0 -0
  84. {anndata-0.12.0rc4 → anndata-0.12.1}/ci/scripts/towncrier_automation.py +0 -0
  85. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/Makefile +0 -0
  86. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/_key_contributors.rst +0 -0
  87. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/_static/img/anndata_schema.svg +0 -0
  88. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/_templates/autosummary/class.rst +0 -0
  89. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/api.md +0 -0
  90. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/benchmark-read-write.ipynb +0 -0
  91. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/benchmarks.md +0 -0
  92. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/concatenation.rst +0 -0
  93. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/contributing.md +0 -0
  94. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/extensions/autosummary_skip_inherited.py +0 -0
  95. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/extensions/no_skip_abc_members.py +0 -0
  96. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/extensions/patch_myst_cite.py +0 -0
  97. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/fileformat-prose.md +0 -0
  98. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/index.md +0 -0
  99. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/interoperability.md +0 -0
  100. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/news.md +0 -0
  101. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/references.rst +0 -0
  102. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.0.md +0 -0
  103. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.1.md +0 -0
  104. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.2.md +0 -0
  105. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.3.md +0 -0
  106. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.4.md +0 -0
  107. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.5.md +0 -0
  108. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.6.md +0 -0
  109. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.7.md +0 -0
  110. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.8.md +0 -0
  111. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.10.9.md +0 -0
  112. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.11.0.md +0 -0
  113. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.11.1.md +0 -0
  114. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.11.2.md +0 -0
  115. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.11.3.md +0 -0
  116. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.11.4.md +0 -0
  117. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.4.0.md +0 -0
  118. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.5.0.md +0 -0
  119. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.6.0.md +0 -0
  120. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.6.x.md +0 -0
  121. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.0.md +0 -0
  122. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.2.md +0 -0
  123. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.3.md +0 -0
  124. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.4.md +0 -0
  125. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.5.md +0 -0
  126. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.6.md +0 -0
  127. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.7.md +0 -0
  128. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.7.8.md +0 -0
  129. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.8.0.md +0 -0
  130. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.9.0.md +0 -0
  131. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.9.1.md +0 -0
  132. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/0.9.2.md +0 -0
  133. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/release-notes/index.md +0 -0
  134. {anndata-0.12.0rc4 → anndata-0.12.1}/docs/tutorials/index.md +0 -0
  135. {anndata-0.12.0rc4 → anndata-0.12.1}/hatch.toml +0 -0
  136. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/__init__.py +0 -0
  137. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/__init__.py +0 -0
  138. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/access.py +0 -0
  139. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/aligned_mapping.py +0 -0
  140. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/extensions.py +0 -0
  141. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/file_backing.py +0 -0
  142. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/index.py +0 -0
  143. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/raw.py +0 -0
  144. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/storage.py +0 -0
  145. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_core/views.py +0 -0
  146. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/__init__.py +0 -0
  147. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/specs/__init__.py +0 -0
  148. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/utils.py +0 -0
  149. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_io/write.py +0 -0
  150. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_types.py +0 -0
  151. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_version.py +0 -0
  152. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/_warnings.py +0 -0
  153. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/abc.py +0 -0
  154. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/compat/__init__.py +7 -7
  155. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/__init__.py +0 -0
  156. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/_dispatch_io.py +0 -0
  157. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/backed/__init__.py +0 -0
  158. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/backed/_compat.py +0 -0
  159. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/backed/_io.py +0 -0
  160. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/merge.py +0 -0
  161. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/multi_files/__init__.py +0 -0
  162. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/multi_files/_anncollection.py +0 -0
  163. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/pytorch/__init__.py +0 -0
  164. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/experimental/pytorch/_annloader.py +0 -0
  165. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/io.py +0 -0
  166. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/logging.py +0 -0
  167. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/tests/__init__.py +0 -0
  168. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/types.py +0 -0
  169. {anndata-0.12.0rc4 → anndata-0.12.1}/src/anndata/typing.py +0 -0
  170. {anndata-0.12.0rc4 → anndata-0.12.1}/src/testing/anndata/__init__.py +0 -0
  171. {anndata-0.12.0rc4 → anndata-0.12.1}/src/testing/anndata/_doctest.py +0 -0
  172. {anndata-0.12.0rc4 → anndata-0.12.1}/src/testing/anndata/py.typed +0 -0
  173. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/adata-comments.tsv +0 -0
  174. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/adata.csv +0 -0
  175. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/archives/readme.md +0 -0
  176. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/archives/v0.7.0/adata.h5ad +0 -0
  177. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/archives/v0.7.0/adata.zarr.zip +0 -0
  178. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/archives/v0.7.8/adata.h5ad +0 -0
  179. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/archives/v0.7.8/adata.zarr.zip +0 -0
  180. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/excel.xlsx +0 -0
  181. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/data/umi_tools.tsv.gz +0 -0
  182. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/lazy/test_concat.py +0 -0
  183. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/lazy/test_write.py +0 -0
  184. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_anncollection.py +0 -0
  185. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_annot.py +0 -0
  186. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_backed_dense.py +0 -0
  187. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_concatenate.py +0 -0
  188. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_dask.py +0 -0
  189. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_dask_view_mem.py +0 -0
  190. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_extensions.py +0 -0
  191. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_get_vector.py +0 -0
  192. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_gpu.py +0 -0
  193. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_helpers.py +0 -0
  194. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_inplace_subset.py +0 -0
  195. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_conversion.py +0 -0
  196. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_partial.py +0 -0
  197. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_io_utils.py +0 -0
  198. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_layers.py +0 -0
  199. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_obsmvarm.py +0 -0
  200. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_obspvarp.py +0 -0
  201. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_raw.py +0 -0
  202. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_repr.py +0 -0
  203. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_transpose.py +0 -0
  204. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_uns.py +0 -0
  205. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_views.py +0 -0
  206. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_x.py +0 -0
  207. {anndata-0.12.0rc4 → anndata-0.12.1}/tests/test_xarray.py +0 -0
@@ -47,6 +47,7 @@ jobs:
47
47
  strategy:
48
48
  matrix:
49
49
  env: ${{ fromJSON(needs.get-environments.outputs.envs) }}
50
+ io_mark: ["zarr_io", "not zarr_io"]
50
51
  env: # environment variable for use in codecov’s env_vars tagging
51
52
  ENV_NAME: ${{ matrix.env.name }}
52
53
  steps:
@@ -70,7 +71,7 @@ jobs:
70
71
  run: uvx hatch -v env create ${{ matrix.env.name }}
71
72
 
72
73
  - name: Run tests
73
- run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto --cov --cov-report=xml --junitxml=test-data/test-results.xml
74
+ run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto --cov --cov-report=xml --junitxml=test-data/test-results.xml -m "${{matrix.io_mark}}"
74
75
 
75
76
  - name: Upload coverage data
76
77
  uses: codecov/codecov-action@v5
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/astral-sh/ruff-pre-commit
3
- rev: v0.11.12
3
+ rev: v0.12.4
4
4
  hooks:
5
5
  - id: ruff
6
6
  args: ["--fix"]
@@ -10,7 +10,7 @@ repos:
10
10
  id: ruff
11
11
  args: ["--preview", "--select=PLR0917"]
12
12
  - repo: https://github.com/biomejs/pre-commit
13
- rev: v1.9.4
13
+ rev: v2.1.2
14
14
  hooks:
15
15
  - id: biome-format
16
16
  - repo: https://github.com/ComPWA/taplo-pre-commit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anndata
3
- Version: 0.12.0rc4
3
+ Version: 0.12.1
4
4
  Summary: Annotated data.
5
5
  Project-URL: Documentation, https://anndata.readthedocs.io/
6
6
  Project-URL: Source, https://github.com/scverse/anndata
@@ -28,11 +28,11 @@ Requires-Dist: array-api-compat>=1.7.1
28
28
  Requires-Dist: h5py>=3.8
29
29
  Requires-Dist: legacy-api-wrap
30
30
  Requires-Dist: natsort
31
- Requires-Dist: numpy>=1.25
31
+ Requires-Dist: numpy>=1.26
32
32
  Requires-Dist: packaging>=24.2
33
- Requires-Dist: pandas!=2.1.0rc0,!=2.1.2,>=2.0.0
34
- Requires-Dist: scipy>=1.11
35
- Requires-Dist: zarr!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3,!=3.0.4,!=3.0.5,!=3.0.6,!=3.0.7,<3.1,>=2.18.7
33
+ Requires-Dist: pandas!=2.1.2,>=2.1.0
34
+ Requires-Dist: scipy>=1.12
35
+ Requires-Dist: zarr!=3.0.*,>=2.18.7
36
36
  Provides-Extra: cu11
37
37
  Requires-Dist: cupy-cuda11x; extra == 'cu11'
38
38
  Provides-Extra: cu12
@@ -74,13 +74,12 @@ Requires-Dist: boltons; extra == 'test'
74
74
  Requires-Dist: dask[array]!=2024.8.*,!=2024.9.*,<2025.2.0,>=2023.5.1; extra == 'test'
75
75
  Requires-Dist: dask[distributed]; extra == 'test'
76
76
  Requires-Dist: filelock; extra == 'test'
77
- Requires-Dist: httpx; extra == 'test'
77
+ Requires-Dist: httpx<1.0; extra == 'test'
78
78
  Requires-Dist: joblib; extra == 'test'
79
79
  Requires-Dist: loompy>=3.0.5; extra == 'test'
80
80
  Requires-Dist: matplotlib; extra == 'test'
81
81
  Requires-Dist: openpyxl; extra == 'test'
82
- Requires-Dist: pandas>=2.1.0; extra == 'test'
83
- Requires-Dist: pyarrow; extra == 'test'
82
+ Requires-Dist: pyarrow<21; extra == 'test'
84
83
  Requires-Dist: pytest-cov; extra == 'test'
85
84
  Requires-Dist: pytest-memray; extra == 'test'
86
85
  Requires-Dist: pytest-mock; extra == 'test'
@@ -97,12 +96,12 @@ Requires-Dist: boltons; extra == 'test-min'
97
96
  Requires-Dist: dask[array]!=2024.8.*,!=2024.9.*,<2025.2.0,>=2023.5.1; extra == 'test-min'
98
97
  Requires-Dist: dask[distributed]; extra == 'test-min'
99
98
  Requires-Dist: filelock; extra == 'test-min'
100
- Requires-Dist: httpx; extra == 'test-min'
99
+ Requires-Dist: httpx<1.0; extra == 'test-min'
101
100
  Requires-Dist: joblib; extra == 'test-min'
102
101
  Requires-Dist: loompy>=3.0.5; extra == 'test-min'
103
102
  Requires-Dist: matplotlib; extra == 'test-min'
104
103
  Requires-Dist: openpyxl; extra == 'test-min'
105
- Requires-Dist: pyarrow; extra == 'test-min'
104
+ Requires-Dist: pyarrow<21; extra == 'test-min'
106
105
  Requires-Dist: pytest-cov; extra == 'test-min'
107
106
  Requires-Dist: pytest-memray; extra == 'test-min'
108
107
  Requires-Dist: pytest-mock; extra == 'test-min'
@@ -70,6 +70,8 @@
70
70
  "pytoml": [""],
71
71
  "pytest": [""],
72
72
  "pooch": [""],
73
+ "xarray": [""],
74
+ "dask": [""],
73
75
  // "scanpy": [""],
74
76
  // "psutil": [""]
75
77
  },
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ import tempfile
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ import h5py
8
+ import numpy as np
9
+ import pandas as pd
10
+ import zarr
11
+
12
+ import anndata as ad
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import Callable
16
+
17
+
18
+ class Dataset2D:
19
+ param_names = ("gen_store", "chunks")
20
+ params = (
21
+ (
22
+ lambda: h5py.File(Path(tempfile.mkdtemp()) / "data.h5ad", mode="w"),
23
+ lambda: zarr.open(
24
+ Path(tempfile.mkdtemp()) / "data.zarr", mode="w", zarr_version=2
25
+ ),
26
+ ),
27
+ ((-1,), None),
28
+ )
29
+
30
+ def setup(
31
+ self, gen_store: Callable[[], zarr.Group | h5py.File], chunks: None | tuple[int]
32
+ ):
33
+ self.n_obs = 100000
34
+ df = pd.DataFrame(
35
+ {
36
+ "a": pd.Categorical(np.array(["a"] * self.n_obs)),
37
+ "b": np.arange(self.n_obs),
38
+ },
39
+ index=[f"cell{i}" for i in range(self.n_obs)],
40
+ )
41
+ store = gen_store()
42
+ ad.io.write_elem(store, "obs", df)
43
+ self.ds = ad.experimental.read_elem_lazy(store["obs"], chunks=chunks)
44
+
45
+ def time_getitem_slice(self, *_):
46
+ self.ds.iloc[0 : (self.n_obs // 2)].to_memory()
47
+
48
+ def peakmem_getitem_slice(self, *_):
49
+ self.ds.iloc[0 : (self.n_obs // 2)].to_memory()
50
+
51
+ def time_full_to_memory(self, *_):
52
+ self.ds.to_memory()
53
+
54
+ def peakmem_full_to_memory(self, *_):
55
+ self.ds.to_memory()
56
+
57
+ def time_getitem_bool_mask(self, *_):
58
+ self.ds.iloc[np.random.randint(0, self.n_obs, self.n_obs // 2)].to_memory()
59
+
60
+ def peakmem_getitem_bool_mask(self, *_):
61
+ self.ds.iloc[np.random.randint(0, self.n_obs, self.n_obs // 2)].to_memory()
@@ -4,11 +4,13 @@ from types import MappingProxyType
4
4
 
5
5
  import numpy as np
6
6
  import zarr
7
+ from dask.array.core import Array as DaskArray
7
8
  from scipy import sparse
8
9
 
9
10
  from anndata import AnnData
10
11
  from anndata._core.sparse_dataset import sparse_dataset
11
12
  from anndata._io.specs import write_elem
13
+ from anndata.experimental import read_elem_lazy
12
14
 
13
15
 
14
16
  def make_alternating_mask(n):
@@ -37,27 +39,36 @@ class SparseCSRContiguousSlice:
37
39
  # (10_000, 500)
38
40
  ],
39
41
  _slices.keys(),
42
+ [True, False],
40
43
  )
41
- param_names = ("shape", "slice")
44
+ param_names = ("shape", "slice", "use_dask")
42
45
 
43
- def setup(self, shape: tuple[int, int], slice: str):
46
+ def setup(self, shape: tuple[int, int], slice: str, use_dask: bool): # noqa: FBT001
44
47
  X = sparse.random(
45
48
  *shape, density=0.01, format="csr", random_state=np.random.default_rng(42)
46
49
  )
47
50
  self.slice = self._slices[slice]
48
51
  g = zarr.group()
49
52
  write_elem(g, "X", X)
50
- self.x = sparse_dataset(g["X"])
53
+ self.x = read_elem_lazy(g["X"]) if use_dask else sparse_dataset(g["X"])
51
54
  self.adata = AnnData(self.x)
52
55
 
53
56
  def time_getitem(self, *_):
54
- self.x[self.slice]
57
+ res = self.x[self.slice]
58
+ if isinstance(res, DaskArray):
59
+ res.compute()
55
60
 
56
61
  def peakmem_getitem(self, *_):
57
- self.x[self.slice]
62
+ res = self.x[self.slice]
63
+ if isinstance(res, DaskArray):
64
+ res.compute()
58
65
 
59
66
  def time_getitem_adata(self, *_):
60
- self.adata[self.slice]
67
+ res = self.adata[self.slice]
68
+ if isinstance(res, DaskArray):
69
+ res.compute()
61
70
 
62
71
  def peakmem_getitem_adata(self, *_):
63
- self.adata[self.slice]
72
+ res = self.adata[self.slice]
73
+ if isinstance(res, DaskArray):
74
+ res.compute()
@@ -1,9 +1,9 @@
1
1
  {
2
- "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
2
+ "$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",
3
3
  "formatter": { "useEditorconfig": true },
4
4
  "overrides": [
5
5
  {
6
- "include": ["./.vscode/*.json", "**/*.jsonc", "**/asv.conf.json"],
6
+ "includes": ["./.vscode/*.json", "**/*.jsonc", "**/asv.conf.json"],
7
7
  "json": {
8
8
  "formatter": {
9
9
  "trailingCommas": "all",
@@ -125,18 +125,19 @@ intersphinx_mapping = dict(
125
125
  awkward=("https://awkward-array.org/doc/stable", None),
126
126
  cupy=("https://docs.cupy.dev/en/stable", None),
127
127
  dask=("https://docs.dask.org/en/stable", None),
128
+ fsspec=("https://filesystem-spec.readthedocs.io/en/stable/", None),
128
129
  h5py=("https://docs.h5py.org/en/latest", None),
129
130
  hdf5plugin=("https://hdf5plugin.readthedocs.io/en/latest", None),
131
+ kvikio=("https://docs.rapids.ai/api/kvikio/stable/", None),
130
132
  loompy=("https://linnarssonlab.org/loompy", None),
131
133
  numpy=("https://numpy.org/doc/stable", None),
134
+ obstore=("https://developmentseed.org/obstore/latest/", None),
132
135
  pandas=("https://pandas.pydata.org/pandas-docs/stable", None),
133
136
  python=("https://docs.python.org/3", None),
134
137
  scipy=("https://docs.scipy.org/doc/scipy", None),
135
138
  sklearn=("https://scikit-learn.org/stable", None),
136
- zarr=("https://zarr.readthedocs.io/en/stable/", None),
137
139
  xarray=("https://docs.xarray.dev/en/stable", None),
138
- obstore=("https://developmentseed.org/obstore/latest/", None),
139
- kvikio=("https://docs.rapids.ai/api/kvikio/stable/", None),
140
+ zarr=("https://zarr.readthedocs.io/en/stable/", None),
140
141
  zarrs=("https://zarrs-python.readthedocs.io/en/stable/", None),
141
142
  )
142
143
 
@@ -0,0 +1,51 @@
1
+ (v0.12.0)=
2
+ ### 0.12.0 {small}`2025-07-16`
3
+
4
+ - (v0.12.0rc4)=
5
+ {guilabel}`rc4` 2025-06-18
6
+ - (v0.12.0rc3)=
7
+ {guilabel}`rc3` 2025-05-20
8
+ - (v0.12.0rc2)=
9
+ {guilabel}`rc2` 2025-05-15
10
+ - (v0.12.0rc1)=
11
+ {guilabel}`rc1` 2025-04-09
12
+
13
+ #### Breaking changes
14
+
15
+ - {guilabel}`rc1` Remove `anndata.read` {user}`ilan-gold` ({pr}`1766`)
16
+ - {guilabel}`rc1` Tighten usage of {class}`scipy.sparse.spmatrix` for describing sparse matrices in types and instance checks to only {class}`scipy.sparse.csr_matrix` and {class}`scipy.sparse.csc_matrix` {user}`ilan-gold` ({pr}`1768`)
17
+ - {guilabel}`rc1` Disallow declaration of {class}`~anndata.AnnData` with non-`cs{r,c}` sparse data-structures {user}`ilan-gold` ({pr}`1829`)
18
+ - {guilabel}`rc1` Upgrade all `DeprecationWarning`s to `FutureWarning`s {user}`ilan-gold` ({pr}`1874`)
19
+ - {guilabel}`rc4` Lower bound `xarray` by `2025.06.01`. {class}`pandas.arrays.StringArray` was previously used as the in-memory `nullable-string-array` container in `xarray`, but due to {issue}`pydata/xarray#10419` now uses {class}`numpy.ndarray` with an object data type. {user}`ilan-gold` ({pr}`2008`)
20
+
21
+ #### Bug fixes
22
+
23
+ - Fix {func}`anndata.experimental.backed.Dataset2D.reindex` internal setting {user}`ilan-gold` ({pr}`2018`)
24
+ - {guilabel}`rc1` Disallow writing of {class}`~anndata.experimental.backed.Dataset2D` objects {user}`ilan-gold` ({pr}`1887`)
25
+ - {guilabel}`rc1` Upgrade old deprecation warning to a `FutureWarning` on `BaseCompressedSparseDataset.__setitem__`, showing our intent to remove the feature in the next release. {user}`ilan-gold` ({pr}`1928`)
26
+ - {guilabel}`rc1` Don't use {func}`asyncio.run` internally for any operations {user}`ilan-gold` ({pr}`1933`)
27
+ - {guilabel}`rc1` Disallow forward slashes in keys for writing {user}`ilan-gold` ({pr}`1940`)
28
+ - {guilabel}`rc2` Convert 1d {class}`numpy.ndarray` and {class}`cupy.ndarray`s in {attr}`anndata.AnnData.obsm` and {attr}`anndata.AnnData.varm` to 2d {user}`ilan-gold` ({pr}`1962`)
29
+ - {guilabel}`rc3` Update zarr v3 bound to >3.0.8 to prevent corrupted data {issue}`zarr-developers/zarr-python#3061` {user}`ilan-gold` ({pr}`1993`)
30
+
31
+ #### Features
32
+
33
+ - {guilabel}`rc1` {data}`None` values can now be serialized to `.h5ad` and `.zarr`,
34
+ preserving e.g. {attr}`~anndata.AnnData.uns` structure through saving and loading {user}`flying-sheep` ({pr}`999`)
35
+ - {guilabel}`rc1` Add {func}`~anndata.experimental.read_elem_lazy` (in place of `read_elem_as_dask`) to handle backed dataframes, sparse arrays, and dense arrays, as well as a {func}`~anndata.experimental.read_lazy` to handle reading in as much of the on-disk data as possible to produce a {class}`~anndata.AnnData` object {user}`ilan-gold` ({pr}`1247`)
36
+ - {guilabel}`rc1` Support {mod}`zarr` version 3 python package {user}`ilan-gold` ({pr}`1726`)
37
+ - {guilabel}`rc1` Adopt the Scientific Python [deprecation schedule](https://scientific-python.org/specs/spec-0000/) {user}`ilan-gold` ({pr}`1768`)
38
+ - {guilabel}`rc1` Allow {mod}`zarr` v3 writing of data {user}`ilan-gold` ({pr}`1892`)
39
+ - {guilabel}`rc1` {func}`anndata.register_anndata_namespace` functionality for adding custom functionality to an {class}`~anndata.AnnData` object {user}`srivarra` ({pr}`1870`)
40
+ - {guilabel}`rc2` Allow xarray Datasets to be used for obs/var/obsm/varm. {user}`ilia-kats` ({pr}`1966`)
41
+ - {guilabel}`rc4` {class}`anndata.experimental.backed.Dataset2D` now takes a compositional approach to wrapping {class}`xarray.Dataset` which may have breaking changes over the past release versions. {user}`ilan-gold` ({pr}`1997`)
42
+ - {guilabel}`rc4` Use {attr}`numpy.dtypes.StringDType` with `na_object` set to {attr}`pandas.NA` for nullable string data with {class}`anndata.experimental.backed.Dataset2D` {user}`ilan-gold` ({pr}`2011`)
43
+
44
+ #### Performance
45
+
46
+ - {guilabel}`rc2` Load AnnLoader lazily to prevent expensive unnecessary `torch` imports when its available on the system. {user}`Zethson` & {user}`flying-sheep` ({pr}`1950`)
47
+ - {guilabel}`rc4` Improve {func}`~anndata.experimental.read_elem_lazy` performance for `h5ad` files by not caching `indptr`. {user}`ilan-gold` ({pr}`2005`)
48
+
49
+ #### Development
50
+
51
+ - {guilabel}`rc4` Temporarily bound {mod}`zarr` to `<3.1` until {pr}`1995` is merged to handle the new data type structure. {user}`ilan-gold` ({pr}`2013`)
@@ -0,0 +1,10 @@
1
+ (v0.12.1)=
2
+ ### 0.12.1 {small}`2025-07-23`
3
+
4
+ ### Bug fixes
5
+
6
+ - Fix `chunks` argument for {func}`anndata.experimental.read_elem_lazy` so that it uses the on-disk chunking when possible, and allow users to pass this argument through to the reading of {class}`anndata.experimental.backed.Dataset2D` {user}`ilan-gold` ({pr}`2033`)
7
+
8
+ ### Performance
9
+
10
+ - Improve integer indexing performance of `h5` 1d arrays that are opened via {func}`anndata.experimental.read_elem_lazy` {user}`ilan-gold` ({pr}`2035`)
@@ -4,6 +4,23 @@
4
4
  Users should notice a significant performance improvement, especially for cloud data, but also likely for local data as well.
5
5
  Here is a quick guide on some of our learnings so far:
6
6
 
7
+ ## Consolidated Metadata
8
+
9
+ All `zarr` stores are now consolidated by default when written via {func}`anndata.io.write_zarr` or {meth}`anndata.AnnData.write_zarr`. For more information on this topic, please seee {ref}`the zarr docs <zarr:user-guide-consolidated-metadata>`. Practcally, this changes means that once a store has been written, it should be treated as immutable **unless you remove the consolidated metadata and/or rewrite after the mutating operation** i.e., if you wish to use `anndata.io.write_elem` to add a column to `obs`, a `layer` etc. to an existing store. For example, to mutate an existing store on-disk, you may do:
10
+
11
+ ```python
12
+ g = zarr.open_group(orig_path, mode="a", use_consolidated=False)
13
+ ad.io.write_elem(
14
+ g,
15
+ "obs",
16
+ obs,
17
+ dataset_kwargs=dict(chunks=(250,)),
18
+ )
19
+ zarr.consolidate_metadata(g.store)
20
+ ```
21
+
22
+ In this example, the store was opened unconsolidated (trying to open it as a consolidated store would error out), edited, and then reconsolidated. Alternatively, one could simple delete the file containing the consolidated metadata first at the root, `.zmetadata`.
23
+
7
24
  ## Remote data
8
25
 
9
26
  We now provide the {func}`anndata.experimental.read_lazy` feature for reading as much of the {class}`~anndata.AnnData` object as lazily as possible, using `dask` and {mod}`xarray`.
@@ -36,18 +36,16 @@ classifiers = [
36
36
  "Topic :: Scientific/Engineering :: Visualization",
37
37
  ]
38
38
  dependencies = [
39
- # pandas 2.1.0rc0 has pandas/issues/54622
40
- "pandas >=2.0.0, !=2.1.0rc0, !=2.1.2",
41
- "numpy>=1.25",
39
+ "pandas >=2.1.0, !=2.1.2",
40
+ "numpy>=1.26",
42
41
  # https://github.com/scverse/anndata/issues/1434
43
- "scipy >=1.11",
42
+ "scipy >=1.12",
44
43
  "h5py>=3.8",
45
44
  "natsort",
46
45
  "packaging>=24.2",
47
46
  "array_api_compat>=1.7.1",
48
47
  "legacy-api-wrap",
49
- # <3.1 on account of https://github.com/scverse/anndata/pull/1995
50
- "zarr >=2.18.7, !=3.0.0, !=3.0.1, !=3.0.2, !=3.0.3, !=3.0.4, !=3.0.5, !=3.0.6, !=3.0.7, <3.1",
48
+ "zarr >=2.18.7, !=3.0.*",
51
49
  ]
52
50
  dynamic = [ "version" ]
53
51
 
@@ -96,16 +94,14 @@ test-min = [
96
94
  "joblib",
97
95
  "boltons",
98
96
  "scanpy>=1.10",
99
- "httpx", # For data downloading
97
+ # TODO: Is 1.0dev1 a real pre-release? https://pypi.org/project/httpx/#history
98
+ "httpx<1.0", # For data downloading
100
99
  "dask[distributed]",
101
100
  "awkward>=2.3.2",
102
- "pyarrow",
101
+ "pyarrow<21", # https://github.com/scikit-hep/awkward/issues/3579
103
102
  "anndata[dask]",
104
103
  ]
105
- test = [
106
- "anndata[test-min,lazy]",
107
- "pandas>=2.1.0",
108
- ] # pandas 2.1.0 needs to be specified for xarray to work with min-deps script
104
+ test = [ "anndata[test-min,lazy]" ]
109
105
  gpu = [ "cupy" ]
110
106
  cu12 = [ "cupy-cuda12x" ]
111
107
  cu11 = [ "cupy-cuda11x" ]
@@ -172,7 +168,7 @@ testpaths = [
172
168
  ]
173
169
  # For some reason this effects how logging is shown when tests are run
174
170
  xfail_strict = true
175
- markers = [ "gpu: mark test to run on GPU" ]
171
+ markers = [ "gpu: mark test to run on GPU", "zarr_io: mark tests that involve zarr io" ]
176
172
 
177
173
  [tool.ruff]
178
174
  src = [ "src" ]
@@ -212,6 +208,7 @@ ignore = [
212
208
  "E731", # Do not assign a lambda expression, use a def -> AnnData allows lambda expression assignments,
213
209
  "E741", # allow I, O, l as variable names -> I is the identity matrix, i, j, k, l is reasonable indexing notation
214
210
  "TID252", # We use relative imports from parent modules
211
+ "PLC0415", # We use a lot of non-top-level imports
215
212
  "PLR2004", # “2” is often not too “magic” a number
216
213
  "PLW2901", # Shadowing loop variables isn’t a big deal
217
214
  ]
@@ -59,7 +59,7 @@ def _gen_dataframe_mapping(
59
59
  df = pd.DataFrame(
60
60
  anno,
61
61
  index=None if length is None else mk_index(length),
62
- columns=None if len(anno) else [],
62
+ columns=None if anno else [],
63
63
  )
64
64
 
65
65
  if length is None:
@@ -62,7 +62,7 @@ if TYPE_CHECKING:
62
62
  from .index import Index
63
63
 
64
64
 
65
- class AnnData(metaclass=utils.DeprecationMixinMeta):
65
+ class AnnData(metaclass=utils.DeprecationMixinMeta): # noqa: PLW1641
66
66
  """\
67
67
  An annotated data matrix.
68
68
 
@@ -904,12 +904,6 @@ def concat_arrays( # noqa: PLR0911, PLR0912
904
904
  ],
905
905
  format="csr",
906
906
  )
907
- scipy_version = Version(scipy.__version__)
908
- # Bug where xstack produces a matrix not an array in 1.11.*
909
- if use_sparse_array and (scipy_version.major, scipy_version.minor) == (1, 11):
910
- if mat.format == "csc":
911
- return sparse.csc_array(mat)
912
- return sparse.csr_array(mat)
913
907
  return mat
914
908
  else:
915
909
  return np.concatenate(
@@ -165,7 +165,11 @@ class BackedSparseMatrix(_cs_matrix):
165
165
  def _get_contiguous_compressed_slice(
166
166
  self, s: slice
167
167
  ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
168
- new_indptr = self.indptr[s.start : s.stop + 1].copy()
168
+ new_indptr = self.indptr[s.start : s.stop + 1]
169
+ # If indptr is cached, we need to make a copy of the subset
170
+ # so as not to alter the underlying cached data.
171
+ if isinstance(self.indptr, np.ndarray):
172
+ new_indptr = new_indptr.copy()
169
173
 
170
174
  start = new_indptr[0]
171
175
  stop = new_indptr[-1]
@@ -245,7 +245,7 @@ class Dataset2D:
245
245
  if df.index.name != index_key and index_key is not None:
246
246
  df = df.set_index(index_key)
247
247
  for col in set(self.columns) - non_nullable_string_cols:
248
- df[col] = pd.array(self[col].data, dtype="string")
248
+ df[col] = df[col].astype(dtype="string")
249
249
  df.index.name = None # matches old AnnData object
250
250
  return df
251
251
 
@@ -389,9 +389,12 @@ class Dataset2D:
389
389
  }
390
390
  el = self.ds.drop_vars(extension_arrays.keys())
391
391
  el = el.reindex({index_dim: index}, method=None, fill_value=fill_value)
392
- for col in self.ds:
393
- el[col] = pd.Series(self.ds[col], index=self.index).reindex(
394
- index, fill_value=fill_value
392
+ for col, data in extension_arrays.items():
393
+ el[col] = XDataArray.from_series(
394
+ pd.Series(data.data, index=self.index).reindex(
395
+ index.rename(self.index.name) if index is not None else index,
396
+ fill_value=fill_value,
397
+ )
395
398
  )
396
399
  return Dataset2D(el)
397
400
 
@@ -176,7 +176,7 @@ def read_h5ad_backed(
176
176
 
177
177
  def read_h5ad(
178
178
  filename: PathLike[str] | str,
179
- backed: Literal["r", "r+"] | bool | None = None,
179
+ backed: Literal["r", "r+"] | bool | None = None, # noqa: FBT001
180
180
  *,
181
181
  as_sparse: Sequence[str] = (),
182
182
  as_sparse_fmt: type[CSMatrix] = sparse.csr_matrix,
@@ -22,9 +22,11 @@ if TYPE_CHECKING:
22
22
  from collections.abc import Generator, Iterable, Iterator, Mapping
23
23
 
24
24
 
25
+ @old_positionals("first_column_names", "dtype")
25
26
  def read_csv(
26
27
  filename: PathLike[str] | str | Iterator[str],
27
28
  delimiter: str | None = ",",
29
+ *,
28
30
  first_column_names: bool | None = None,
29
31
  dtype: str = "float32",
30
32
  ) -> AnnData:
@@ -331,9 +333,11 @@ def read_mtx(filename: PathLike[str] | str, dtype: str = "float32") -> AnnData:
331
333
  return AnnData(X)
332
334
 
333
335
 
336
+ @old_positionals("first_column_names", "dtype")
334
337
  def read_text(
335
338
  filename: PathLike[str] | str | Iterator[str],
336
339
  delimiter: str | None = None,
340
+ *,
337
341
  first_column_names: bool | None = None,
338
342
  dtype: str = "float32",
339
343
  ) -> AnnData:
@@ -381,7 +385,7 @@ def _iter_lines(file_like: Iterable[str]) -> Generator[str, None, None]:
381
385
  def _read_text( # noqa: PLR0912, PLR0915
382
386
  f: Iterator[str],
383
387
  delimiter: str | None,
384
- first_column_names: bool | None,
388
+ first_column_names: bool | None, # noqa: FBT001
385
389
  dtype: str,
386
390
  ) -> AnnData:
387
391
  comments = []
@@ -132,7 +132,7 @@ def read_sparse_as_dask(
132
132
  path_or_sparse_dataset = (
133
133
  Path(filename(elem))
134
134
  if isinstance(elem, H5Group)
135
- else ad.io.sparse_dataset(elem)
135
+ else ad.io.sparse_dataset(elem, should_cache_indptr=False)
136
136
  )
137
137
  elem_name = get_elem_name(elem)
138
138
  shape: tuple[int, int] = tuple(elem.attrs["shape"])
@@ -177,21 +177,37 @@ def read_sparse_as_dask(
177
177
  return da_mtx
178
178
 
179
179
 
180
+ def resolve_chunks(
181
+ elem: H5Array | ZarrArray,
182
+ chunks_arg: tuple[int, ...] | None,
183
+ shape: tuple[int, ...],
184
+ ) -> tuple[int, ...]:
185
+ shape = tuple(elem.shape)
186
+ if chunks_arg is not None:
187
+ # None and -1 on a given axis indicate that one should use the shape
188
+ # in `dask`'s semantics.
189
+ return tuple(
190
+ c if c not in {None, -1} else s
191
+ for c, s in zip(chunks_arg, shape, strict=True)
192
+ )
193
+ elif elem.chunks is None: # h5 unchunked
194
+ return tuple(min(_DEFAULT_STRIDE, s) for s in shape)
195
+ return elem.chunks
196
+
197
+
180
198
  @_LAZY_REGISTRY.register_read(H5Array, IOSpec("string-array", "0.2.0"))
181
199
  def read_h5_string_array(
182
200
  elem: H5Array,
183
201
  *,
184
202
  _reader: LazyReader,
185
- chunks: tuple[int, int] | None = None,
203
+ chunks: tuple[int] | None = None,
186
204
  ) -> DaskArray:
187
205
  import dask.array as da
188
206
 
189
207
  from anndata._io.h5ad import read_dataset
190
208
 
191
- return da.from_array(
192
- read_dataset(elem),
193
- chunks=chunks if chunks is not None else (_DEFAULT_STRIDE,) * len(elem.shape),
194
- )
209
+ chunks = resolve_chunks(elem, chunks, tuple(elem.shape))
210
+ return da.from_array(read_dataset(elem), chunks=chunks)
195
211
 
196
212
 
197
213
  @_LAZY_REGISTRY.register_read(H5Array, IOSpec("array", "0.2.0"))
@@ -204,13 +220,7 @@ def read_h5_array(
204
220
  elem_name: str = elem.name
205
221
  shape = tuple(elem.shape)
206
222
  dtype = elem.dtype
207
- chunks = (
208
- tuple(
209
- c if c not in {None, -1} else s for c, s in zip(chunks, shape, strict=True)
210
- )
211
- if chunks is not None
212
- else tuple(min(_DEFAULT_STRIDE, s) for s in shape)
213
- )
223
+ chunks = resolve_chunks(elem, chunks, shape)
214
224
 
215
225
  chunk_layout = tuple(
216
226
  compute_chunk_layout_for_axis_size(chunks[i], shape[i])
@@ -228,7 +238,6 @@ def read_h5_array(
228
238
  def read_zarr_array(
229
239
  elem: ZarrArray, *, _reader: LazyReader, chunks: tuple[int, ...] | None = None
230
240
  ) -> DaskArray:
231
- chunks: tuple[int, ...] = chunks if chunks is not None else elem.chunks
232
241
  import dask.array as da
233
242
 
234
243
  return da.from_zarr(elem, chunks=chunks)
@@ -284,9 +293,10 @@ def read_dataframe(
284
293
  *,
285
294
  _reader: LazyReader,
286
295
  use_range_index: bool = False,
296
+ chunks: tuple[int] | None = None,
287
297
  ) -> Dataset2D:
288
298
  elem_dict = {
289
- k: _reader.read_elem(elem[k])
299
+ k: _reader.read_elem(elem[k], chunks=chunks)
290
300
  for k in [*elem.attrs["column-order"], elem.attrs["_index"]]
291
301
  }
292
302
  # If we use a range index, the coord axis needs to have the special dim name