anndata 0.12.0rc4__tar.gz → 0.12.2__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 (211) hide show
  1. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/test-cpu.yml +6 -2
  2. {anndata-0.12.0rc4 → anndata-0.12.2}/.pre-commit-config.yaml +2 -2
  3. {anndata-0.12.0rc4 → anndata-0.12.2}/PKG-INFO +9 -10
  4. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/asv.conf.json +2 -0
  5. anndata-0.12.2/benchmarks/benchmarks/dataset2d.py +61 -0
  6. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/benchmarks/sparse_dataset.py +18 -7
  7. {anndata-0.12.0rc4 → anndata-0.12.2}/biome.jsonc +2 -2
  8. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/conf.py +4 -3
  9. anndata-0.12.2/docs/release-notes/0.12.0.md +51 -0
  10. anndata-0.12.2/docs/release-notes/0.12.1.md +10 -0
  11. anndata-0.12.2/docs/release-notes/0.12.2.md +6 -0
  12. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/tutorials/zarr-v3.md +19 -2
  13. {anndata-0.12.0rc4 → anndata-0.12.2}/hatch.toml +1 -0
  14. {anndata-0.12.0rc4 → anndata-0.12.2}/pyproject.toml +13 -13
  15. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/aligned_df.py +1 -1
  16. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/anndata.py +23 -6
  17. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/index.py +6 -13
  18. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/merge.py +0 -6
  19. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/raw.py +5 -3
  20. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/sparse_dataset.py +5 -1
  21. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/views.py +20 -14
  22. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/xarray.py +20 -16
  23. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/h5ad.py +48 -23
  24. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/read.py +20 -5
  25. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/specs/lazy_methods.py +25 -15
  26. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/specs/methods.py +22 -20
  27. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/specs/registry.py +15 -4
  28. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/zarr.py +0 -20
  29. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_settings.py +6 -3
  30. anndata-0.12.2/src/anndata/_settings.pyi +49 -0
  31. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/compat/__init__.py +36 -11
  32. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/backed/_lazy_arrays.py +27 -5
  33. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/tests/helpers.py +52 -50
  34. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/utils.py +3 -4
  35. {anndata-0.12.0rc4 → anndata-0.12.2}/src/testing/anndata/_pytest.py +4 -0
  36. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/conftest.py +15 -2
  37. anndata-0.12.2/tests/data/archives/v0.11.4/adata.h5ad +0 -0
  38. anndata-0.12.2/tests/data/archives/v0.11.4/adata.zarr.zip +0 -0
  39. anndata-0.12.2/tests/data/archives/v0.11.4/readme.md +10 -0
  40. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/lazy/conftest.py +12 -3
  41. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/lazy/test_read.py +33 -3
  42. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_awkward.py +2 -1
  43. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_backed_hdf5.py +9 -6
  44. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_backed_sparse.py +19 -15
  45. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_base.py +4 -2
  46. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_concatenate_disk.py +12 -5
  47. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_deprecations.py +2 -2
  48. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_backwards_compat.py +29 -6
  49. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_dispatched.py +10 -5
  50. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_elementwise.py +85 -27
  51. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_partial.py +0 -6
  52. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_warnings.py +2 -2
  53. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_readwrite.py +65 -11
  54. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_settings.py +19 -0
  55. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_structured_arrays.py +24 -24
  56. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_utils.py +4 -2
  57. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_views.py +33 -15
  58. anndata-0.12.0rc4/docs/release-notes/0.12.0rc1.md +0 -26
  59. anndata-0.12.0rc4/docs/release-notes/0.12.0rc2.md +0 -14
  60. anndata-0.12.0rc4/docs/release-notes/0.12.0rc3.md +0 -6
  61. anndata-0.12.0rc4/docs/release-notes/0.12.0rc4.md +0 -15
  62. anndata-0.12.0rc4/docs/release-notes/2013.development.md +0 -1
  63. {anndata-0.12.0rc4 → anndata-0.12.2}/.cirun.yml +0 -0
  64. {anndata-0.12.0rc4 → anndata-0.12.2}/.codecov.yml +0 -0
  65. {anndata-0.12.0rc4 → anndata-0.12.2}/.editorconfig +0 -0
  66. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  67. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  68. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/ISSUE_TEMPLATE/enhancement-request.yml +0 -0
  69. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  70. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  71. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/benchmark.yml +0 -0
  72. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/check-pr-milestoned.yml +0 -0
  73. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/close-stale.yml +0 -0
  74. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/codespell.yml +0 -0
  75. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/label-stale.yml +0 -0
  76. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/publish.yml +0 -0
  77. {anndata-0.12.0rc4 → anndata-0.12.2}/.github/workflows/test-gpu.yml +0 -0
  78. {anndata-0.12.0rc4 → anndata-0.12.2}/.gitignore +0 -0
  79. {anndata-0.12.0rc4 → anndata-0.12.2}/.gitmodules +0 -0
  80. {anndata-0.12.0rc4 → anndata-0.12.2}/.prettierignore +0 -0
  81. {anndata-0.12.0rc4 → anndata-0.12.2}/.prettierrc.yaml +0 -0
  82. {anndata-0.12.0rc4 → anndata-0.12.2}/.readthedocs.yml +0 -0
  83. {anndata-0.12.0rc4 → anndata-0.12.2}/.taplo.toml +0 -0
  84. {anndata-0.12.0rc4 → anndata-0.12.2}/.vscode/launch.json +0 -0
  85. {anndata-0.12.0rc4 → anndata-0.12.2}/.vscode/settings.json +0 -0
  86. {anndata-0.12.0rc4 → anndata-0.12.2}/LICENSE +0 -0
  87. {anndata-0.12.0rc4 → anndata-0.12.2}/README.md +0 -0
  88. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/README.md +0 -0
  89. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/benchmarks/__init__.py +0 -0
  90. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/benchmarks/anndata.py +0 -0
  91. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/benchmarks/readwrite.py +0 -0
  92. {anndata-0.12.0rc4 → anndata-0.12.2}/benchmarks/benchmarks/utils.py +0 -0
  93. {anndata-0.12.0rc4 → anndata-0.12.2}/ci/constraints.txt +0 -0
  94. {anndata-0.12.0rc4 → anndata-0.12.2}/ci/scripts/min-deps.py +0 -0
  95. {anndata-0.12.0rc4 → anndata-0.12.2}/ci/scripts/towncrier_automation.py +0 -0
  96. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/Makefile +0 -0
  97. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/_key_contributors.rst +0 -0
  98. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/_static/img/anndata_schema.svg +0 -0
  99. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/_templates/autosummary/class.rst +0 -0
  100. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/api.md +0 -0
  101. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/benchmark-read-write.ipynb +0 -0
  102. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/benchmarks.md +0 -0
  103. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/concatenation.rst +0 -0
  104. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/contributing.md +0 -0
  105. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/extensions/autosummary_skip_inherited.py +0 -0
  106. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/extensions/no_skip_abc_members.py +0 -0
  107. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/extensions/patch_myst_cite.py +0 -0
  108. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/fileformat-prose.md +0 -0
  109. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/index.md +0 -0
  110. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/interoperability.md +0 -0
  111. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/news.md +0 -0
  112. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/references.rst +0 -0
  113. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.0.md +0 -0
  114. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.1.md +0 -0
  115. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.2.md +0 -0
  116. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.3.md +0 -0
  117. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.4.md +0 -0
  118. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.5.md +0 -0
  119. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.6.md +0 -0
  120. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.7.md +0 -0
  121. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.8.md +0 -0
  122. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.10.9.md +0 -0
  123. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.11.0.md +0 -0
  124. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.11.1.md +0 -0
  125. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.11.2.md +0 -0
  126. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.11.3.md +0 -0
  127. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.11.4.md +0 -0
  128. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.4.0.md +0 -0
  129. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.5.0.md +0 -0
  130. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.6.0.md +0 -0
  131. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.6.x.md +0 -0
  132. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.0.md +0 -0
  133. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.2.md +0 -0
  134. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.3.md +0 -0
  135. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.4.md +0 -0
  136. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.5.md +0 -0
  137. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.6.md +0 -0
  138. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.7.md +0 -0
  139. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.7.8.md +0 -0
  140. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.8.0.md +0 -0
  141. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.9.0.md +0 -0
  142. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.9.1.md +0 -0
  143. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/0.9.2.md +0 -0
  144. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/release-notes/index.md +0 -0
  145. {anndata-0.12.0rc4 → anndata-0.12.2}/docs/tutorials/index.md +0 -0
  146. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/__init__.py +0 -0
  147. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/__init__.py +0 -0
  148. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/access.py +0 -0
  149. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/aligned_mapping.py +0 -0
  150. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/extensions.py +0 -0
  151. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/file_backing.py +0 -0
  152. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_core/storage.py +0 -0
  153. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/__init__.py +0 -0
  154. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/specs/__init__.py +0 -0
  155. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/utils.py +0 -0
  156. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_io/write.py +0 -0
  157. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_types.py +0 -0
  158. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_version.py +0 -0
  159. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/_warnings.py +0 -0
  160. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/abc.py +0 -0
  161. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/__init__.py +0 -0
  162. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/_dispatch_io.py +0 -0
  163. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/backed/__init__.py +0 -0
  164. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/backed/_compat.py +0 -0
  165. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/backed/_io.py +0 -0
  166. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/merge.py +0 -0
  167. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/multi_files/__init__.py +0 -0
  168. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/multi_files/_anncollection.py +0 -0
  169. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/pytorch/__init__.py +0 -0
  170. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/experimental/pytorch/_annloader.py +0 -0
  171. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/io.py +0 -0
  172. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/logging.py +0 -0
  173. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/tests/__init__.py +0 -0
  174. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/types.py +0 -0
  175. {anndata-0.12.0rc4 → anndata-0.12.2}/src/anndata/typing.py +0 -0
  176. {anndata-0.12.0rc4 → anndata-0.12.2}/src/testing/anndata/__init__.py +0 -0
  177. {anndata-0.12.0rc4 → anndata-0.12.2}/src/testing/anndata/_doctest.py +0 -0
  178. {anndata-0.12.0rc4 → anndata-0.12.2}/src/testing/anndata/py.typed +0 -0
  179. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/adata-comments.tsv +0 -0
  180. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/adata.csv +0 -0
  181. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/archives/readme.md +0 -0
  182. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/archives/v0.7.0/adata.h5ad +0 -0
  183. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/archives/v0.7.0/adata.zarr.zip +0 -0
  184. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/archives/v0.7.8/adata.h5ad +0 -0
  185. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/archives/v0.7.8/adata.zarr.zip +0 -0
  186. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/excel.xlsx +0 -0
  187. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/data/umi_tools.tsv.gz +0 -0
  188. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/lazy/test_concat.py +0 -0
  189. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/lazy/test_write.py +0 -0
  190. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_anncollection.py +0 -0
  191. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_annot.py +0 -0
  192. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_backed_dense.py +0 -0
  193. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_concatenate.py +0 -0
  194. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_dask.py +0 -0
  195. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_dask_view_mem.py +0 -0
  196. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_extensions.py +0 -0
  197. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_get_vector.py +0 -0
  198. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_gpu.py +0 -0
  199. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_helpers.py +0 -0
  200. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_inplace_subset.py +0 -0
  201. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_conversion.py +0 -0
  202. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_io_utils.py +0 -0
  203. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_layers.py +0 -0
  204. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_obsmvarm.py +0 -0
  205. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_obspvarp.py +0 -0
  206. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_raw.py +0 -0
  207. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_repr.py +0 -0
  208. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_transpose.py +0 -0
  209. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_uns.py +0 -0
  210. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_x.py +0 -0
  211. {anndata-0.12.0rc4 → anndata-0.12.2}/tests/test_xarray.py +0 -0
@@ -38,7 +38,7 @@ jobs:
38
38
  ENVS_JSON=$(NO_COLOR=1 uvx hatch env show --json | jq -c 'to_entries
39
39
  | map(
40
40
  select(.key | startswith("hatch-test"))
41
- | { name: .key, python: .value.python }
41
+ | { name: .key, python: .value.python, args: (.value."extra-args" // [] | join(" ")) }
42
42
  )')
43
43
  echo "envs=${ENVS_JSON}" | tee $GITHUB_OUTPUT
44
44
  test:
@@ -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:
@@ -55,6 +56,9 @@ jobs:
55
56
  fetch-depth: 0
56
57
  filter: blob:none
57
58
 
59
+ - name: Install system dependencies
60
+ run: sudo apt install -y hdf5-tools
61
+
58
62
  - name: Set up Python ${{ matrix.env.python }}
59
63
  uses: actions/setup-python@v5
60
64
  with:
@@ -70,7 +74,7 @@ jobs:
70
74
  run: uvx hatch -v env create ${{ matrix.env.name }}
71
75
 
72
76
  - 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
77
+ 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 }}" ${{ matrix.env.args }}
74
78
 
75
79
  - name: Upload coverage data
76
80
  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.2
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`)
@@ -0,0 +1,6 @@
1
+ (v0.12.2)=
2
+ ### 0.12.2 {small}`2025-08-11`
3
+
4
+ ### Bug fixes
5
+
6
+ - Revert accidental change where {attr}`~anndata.AnnData.X` got written to disk when it was `None` {user}`flying-sheep` ({pr}`2054`)
@@ -1,9 +1,26 @@
1
1
  # zarr-v3 Guide/Roadmap
2
2
 
3
- `anndata` now uses the much improved {mod}`zarr` v3 package and also allows writing of datasets in the v3 format via {attr}`anndata.settings.zarr_write_format`, with the exception of structured arrays.
3
+ `anndata` now uses the much improved {mod}`zarr` v3 package and also allows writing of datasets in the v3 format via {attr}`anndata.settings.zarr_write_format` via {func}`anndata.io.write_zarr` or {meth}`anndata.AnnData.write_zarr`, with the exception of structured arrays.
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`.
@@ -31,7 +48,7 @@ import anndata as ad
31
48
  from collections.abc import Mapping
32
49
  from typing import Any
33
50
 
34
- ad.settings.zarr_write_format = 3 # Absolutely crucial! Sharding is only for the v3 file format!
51
+ g = zarr.open_group(orig_path, mode="a", use_consolidated=False, zarr_version=3) # zarr_version 3 is default but note that sharding only works with v3!
35
52
 
36
53
  def write_sharded(group: zarr.Group, adata: ad.AnnData):
37
54
  def callback(
@@ -36,6 +36,7 @@ overrides.matrix.deps.python = [
36
36
  overrides.matrix.deps.features = [
37
37
  { if = [ "stable", "pre" ], value = "test" },
38
38
  ]
39
+ overrides.matrix.deps.extra-args = { if = [ "stable", "pre" ], value = [ "--strict-warnings" ] }
39
40
 
40
41
  [[envs.hatch-test.matrix]]
41
42
  deps = [ "stable", "pre", "min" ]
@@ -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" ]
@@ -150,6 +146,7 @@ addopts = [
150
146
  filterwarnings = [
151
147
  "ignore::anndata._warnings.OldFormatWarning",
152
148
  "ignore::anndata._warnings.ExperimentalFeatureWarning",
149
+ "ignore:.*first_column_names:FutureWarning:scanpy", # scanpy 1.10.x
153
150
  ]
154
151
  # When `--strict-warnings` is used, all warnings are treated as errors, except those:
155
152
  filterwarnings_when_strict = [
@@ -162,6 +159,8 @@ filterwarnings_when_strict = [
162
159
  "default:The codec `vlen-utf8:UserWarning",
163
160
  "default:The dtype `StringDType():UserWarning",
164
161
  "default:Consolidated metadata is:UserWarning",
162
+ "default:.*Structured:zarr.core.dtype.common.UnstableSpecificationWarning",
163
+ "default:.*FixedLengthUTF32:zarr.core.dtype.common.UnstableSpecificationWarning",
165
164
  ]
166
165
  python_files = "test_*.py"
167
166
  testpaths = [
@@ -172,7 +171,7 @@ testpaths = [
172
171
  ]
173
172
  # For some reason this effects how logging is shown when tests are run
174
173
  xfail_strict = true
175
- markers = [ "gpu: mark test to run on GPU" ]
174
+ markers = [ "gpu: mark test to run on GPU", "zarr_io: mark tests that involve zarr io" ]
176
175
 
177
176
  [tool.ruff]
178
177
  src = [ "src" ]
@@ -212,6 +211,7 @@ ignore = [
212
211
  "E731", # Do not assign a lambda expression, use a def -> AnnData allows lambda expression assignments,
213
212
  "E741", # allow I, O, l as variable names -> I is the identity matrix, i, j, k, l is reasonable indexing notation
214
213
  "TID252", # We use relative imports from parent modules
214
+ "PLC0415", # We use a lot of non-top-level imports
215
215
  "PLR2004", # “2” is often not too “magic” a number
216
216
  "PLW2901", # Shadowing loop variables isn’t a big deal
217
217
  ]
@@ -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:
@@ -56,13 +56,13 @@ if TYPE_CHECKING:
56
56
 
57
57
  from zarr.storage import StoreLike
58
58
 
59
- from ..compat import Index1D, XDataset
59
+ from ..compat import Index1D, Index1DNorm, XDataset
60
60
  from ..typing import XDataType
61
61
  from .aligned_mapping import AxisArraysView, LayersView, PairwiseArraysView
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
 
@@ -197,6 +197,11 @@ class AnnData(metaclass=utils.DeprecationMixinMeta):
197
197
 
198
198
  _accessors: ClassVar[set[str]] = set()
199
199
 
200
+ # view attributes
201
+ _adata_ref: AnnData | None
202
+ _oidx: Index1DNorm | None
203
+ _vidx: Index1DNorm | None
204
+
200
205
  @old_positionals(
201
206
  "obsm",
202
207
  "varm",
@@ -226,8 +231,8 @@ class AnnData(metaclass=utils.DeprecationMixinMeta):
226
231
  asview: bool = False,
227
232
  obsp: np.ndarray | Mapping[str, Sequence[Any]] | None = None,
228
233
  varp: np.ndarray | Mapping[str, Sequence[Any]] | None = None,
229
- oidx: Index1D | None = None,
230
- vidx: Index1D | None = None,
234
+ oidx: Index1DNorm | int | np.integer | None = None,
235
+ vidx: Index1DNorm | int | np.integer | None = None,
231
236
  ):
232
237
  # check for any multi-indices that aren’t later checked in coerce_array
233
238
  for attr, key in [(obs, "obs"), (var, "var"), (X, "X")]:
@@ -237,6 +242,8 @@ class AnnData(metaclass=utils.DeprecationMixinMeta):
237
242
  if not isinstance(X, AnnData):
238
243
  msg = "`X` has to be an AnnData object."
239
244
  raise ValueError(msg)
245
+ assert oidx is not None
246
+ assert vidx is not None
240
247
  self._init_as_view(X, oidx, vidx)
241
248
  else:
242
249
  self._init_as_actual(
@@ -256,7 +263,12 @@ class AnnData(metaclass=utils.DeprecationMixinMeta):
256
263
  filemode=filemode,
257
264
  )
258
265
 
259
- def _init_as_view(self, adata_ref: AnnData, oidx: Index, vidx: Index):
266
+ def _init_as_view(
267
+ self,
268
+ adata_ref: AnnData,
269
+ oidx: Index1DNorm | int | np.integer,
270
+ vidx: Index1DNorm | int | np.integer,
271
+ ):
260
272
  if adata_ref.isbacked and adata_ref.is_view:
261
273
  msg = (
262
274
  "Currently, you cannot index repeatedly into a backed AnnData, "
@@ -277,6 +289,9 @@ class AnnData(metaclass=utils.DeprecationMixinMeta):
277
289
  vidx += adata_ref.n_vars * (vidx < 0)
278
290
  vidx = slice(vidx, vidx + 1, 1)
279
291
  if adata_ref.is_view:
292
+ assert adata_ref._adata_ref is not None
293
+ assert adata_ref._oidx is not None
294
+ assert adata_ref._vidx is not None
280
295
  prev_oidx, prev_vidx = adata_ref._oidx, adata_ref._vidx
281
296
  adata_ref = adata_ref._adata_ref
282
297
  oidx, vidx = _resolve_idxs((prev_oidx, prev_vidx), (oidx, vidx), adata_ref)
@@ -1004,7 +1019,9 @@ class AnnData(metaclass=utils.DeprecationMixinMeta):
1004
1019
 
1005
1020
  write_attribute(self.file._file, attr, value)
1006
1021
 
1007
- def _normalize_indices(self, index: Index | None) -> tuple[slice, slice]:
1022
+ def _normalize_indices(
1023
+ self, index: Index | None
1024
+ ) -> tuple[Index1DNorm | int | np.integer, Index1DNorm | int | np.integer]:
1008
1025
  return _normalize_indices(index, self.obs_names, self.var_names)
1009
1026
 
1010
1027
  # TODO: this is not quite complete...
@@ -14,18 +14,18 @@ from ..compat import AwkArray, CSArray, CSMatrix, DaskArray, XDataArray
14
14
  from .xarray import Dataset2D
15
15
 
16
16
  if TYPE_CHECKING:
17
- from ..compat import Index, Index1D
17
+ from ..compat import Index, Index1D, Index1DNorm
18
18
 
19
19
 
20
20
  def _normalize_indices(
21
21
  index: Index | None, names0: pd.Index, names1: pd.Index
22
- ) -> tuple[slice, slice]:
22
+ ) -> tuple[Index1DNorm | int | np.integer, Index1DNorm | int | np.integer]:
23
23
  # deal with tuples of length 1
24
24
  if isinstance(index, tuple) and len(index) == 1:
25
25
  index = index[0]
26
26
  # deal with pd.Series
27
27
  if isinstance(index, pd.Series):
28
- index: Index = index.values
28
+ index = index.values
29
29
  if isinstance(index, tuple):
30
30
  # TODO: The series should probably be aligned first
31
31
  index = tuple(i.values if isinstance(i, pd.Series) else i for i in index)
@@ -36,15 +36,8 @@ def _normalize_indices(
36
36
 
37
37
 
38
38
  def _normalize_index( # noqa: PLR0911, PLR0912
39
- indexer: slice
40
- | np.integer
41
- | int
42
- | str
43
- | Sequence[bool | int | np.integer]
44
- | np.ndarray
45
- | pd.Index,
46
- index: pd.Index,
47
- ) -> slice | int | np.ndarray: # ndarray of int or bool
39
+ indexer: Index1D, index: pd.Index
40
+ ) -> Index1DNorm | int | np.integer:
48
41
  # TODO: why is this here? All tests pass without it and it seems at the minimum not strict enough.
49
42
  if not isinstance(index, pd.RangeIndex) and index.dtype in (np.float64, np.int64):
50
43
  msg = f"Don’t call _normalize_index with non-categorical/string names and non-range index {index}"
@@ -212,7 +205,7 @@ def _subset_awkarray(a: AwkArray, subset_idx: Index):
212
205
 
213
206
  # Registration for SparseDataset occurs in sparse_dataset.py
214
207
  @_subset.register(h5py.Dataset)
215
- def _subset_dataset(d, subset_idx):
208
+ def _subset_dataset(d: h5py.Dataset, subset_idx: Index):
216
209
  if not isinstance(subset_idx, tuple):
217
210
  subset_idx = (subset_idx,)
218
211
  ordered = list(subset_idx)
@@ -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(
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  from collections.abc import Mapping, Sequence
18
18
  from typing import ClassVar
19
19
 
20
- from ..compat import CSMatrix
20
+ from ..compat import CSMatrix, Index, Index1DNorm
21
21
  from .aligned_mapping import AxisArraysView
22
22
  from .anndata import AnnData
23
23
  from .sparse_dataset import BaseCompressedSparseDataset
@@ -121,7 +121,7 @@ class Raw:
121
121
  def obs_names(self) -> pd.Index[str]:
122
122
  return self._adata.obs_names
123
123
 
124
- def __getitem__(self, index):
124
+ def __getitem__(self, index: Index) -> Raw:
125
125
  oidx, vidx = self._normalize_indices(index)
126
126
 
127
127
  # To preserve two dimensional shape
@@ -169,7 +169,9 @@ class Raw:
169
169
  uns=self._adata.uns.copy(),
170
170
  )
171
171
 
172
- def _normalize_indices(self, packed_index):
172
+ def _normalize_indices(
173
+ self, packed_index: Index
174
+ ) -> tuple[Index1DNorm | int | np.integer, Index1DNorm | int | np.integer]:
173
175
  # deal with slicing with pd.Series
174
176
  if isinstance(packed_index, pd.Series):
175
177
  packed_index = packed_index.values