spatialdata 0.7.3a1__tar.gz → 0.8.0__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 (175) hide show
  1. spatialdata-0.8.0/.claude/skills/memray/SKILL.md +37 -0
  2. spatialdata-0.8.0/.claude/skills/profimp/SKILL.md +30 -0
  3. spatialdata-0.8.0/.claude/skills/pyspy/SKILL.md +45 -0
  4. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/workflows/test.yaml +8 -8
  5. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.gitignore +9 -2
  6. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.mypy.ini +1 -1
  7. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.pre-commit-config.yaml +3 -3
  8. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/PKG-INFO +12 -8
  9. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/README.md +6 -2
  10. spatialdata-0.8.0/_version.py +24 -0
  11. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/benchmarks/README.md +26 -0
  12. spatialdata-0.8.0/benchmarks/benchmark_dataloader.py +75 -0
  13. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/models_utils.md +1 -0
  14. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/index.md +7 -1
  15. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/pyproject.toml +42 -6
  16. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/_elements.py +3 -22
  17. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/_utils.py +3 -3
  18. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/centroids.py +22 -37
  19. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/concatenate.py +0 -8
  20. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/rasterize_bins.py +4 -8
  21. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/transform.py +35 -12
  22. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/query/_utils.py +5 -6
  23. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/query/relational_query.py +188 -133
  24. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/query/spatial_query.py +119 -120
  25. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/spatialdata.py +44 -22
  26. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/validation.py +4 -0
  27. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/_utils.py +65 -13
  28. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/io_points.py +2 -3
  29. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/io_raster.py +124 -17
  30. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/io_shapes.py +2 -3
  31. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/io_table.py +1 -1
  32. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_types.py +2 -2
  33. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/dataloader/__init__.py +3 -5
  34. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/dataloader/datasets.py +35 -17
  35. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/datasets.py +4 -1
  36. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/models/__init__.py +2 -0
  37. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/models/_utils.py +3 -2
  38. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/models/chunks_utils.py +2 -2
  39. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/models/models.py +58 -5
  40. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/conftest.py +95 -22
  41. spatialdata-0.8.0/tests/core/operations/conftest.py +1 -0
  42. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_rasterize_bins.py +0 -18
  43. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_spatialdata_operations.py +22 -18
  44. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_transform.py +75 -1
  45. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_vectorize.py +25 -22
  46. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/query/test_relational_query.py +39 -3
  47. spatialdata-0.8.0/tests/core/query/test_relational_query_match_sdata_to_table.py +235 -0
  48. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/query/test_spatial_query.py +101 -1
  49. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/test_data_extent.py +13 -9
  50. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/test_get_attrs.py +5 -9
  51. spatialdata-0.8.0/tests/dataloader/conftest.py +1 -0
  52. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_attrs_io.py +2 -0
  53. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_format.py +2 -0
  54. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_metadata.py +5 -0
  55. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_multi_table.py +1 -1
  56. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_partial_read.py +47 -25
  57. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_pyramids_performance.py +22 -1
  58. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_readwrite.py +115 -15
  59. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/test_utils.py +24 -1
  60. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/models/test_models.py +14 -13
  61. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/utils/test_sanitize.py +16 -0
  62. spatialdata-0.7.3a1/_version.py +0 -34
  63. spatialdata-0.7.3a1/docs/_templates/.gitkeep +0 -0
  64. spatialdata-0.7.3a1/docs/_templates/autosummary/base.rst +0 -5
  65. spatialdata-0.7.3a1/docs/_templates/autosummary/class.rst +0 -61
  66. spatialdata-0.7.3a1/docs/_templates/autosummary/function.rst +0 -5
  67. spatialdata-0.7.3a1/tests/core/query/test_relational_query_match_sdata_to_table.py +0 -146
  68. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.bumpversion.cfg +0 -0
  69. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.editorconfig +0 -0
  70. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  71. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  72. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/codecov.yml +0 -0
  73. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/release.yml +0 -0
  74. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/workflows/build_image.yml +0 -0
  75. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.github/workflows/release.yaml +0 -0
  76. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.gitmodules +0 -0
  77. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/.readthedocs.yaml +0 -0
  78. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/CHANGELOG.md +0 -0
  79. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/Dockerfile +0 -0
  80. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/LICENSE +0 -0
  81. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/asv.conf.json +0 -0
  82. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/benchmarks/__init__.py +0 -0
  83. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/benchmarks/benchmark_imports.py +0 -0
  84. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/benchmarks/spatialdata_benchmark.py +0 -0
  85. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/benchmarks/utils.py +0 -0
  86. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/Makefile +0 -0
  87. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/_static/.gitkeep +0 -0
  88. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/_static/css/custom.css +0 -0
  89. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/_static/img/spatialdata_horizontal.png +0 -0
  90. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/SpatialData.md +0 -0
  91. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/data_formats.md +0 -0
  92. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/dataloader.md +0 -0
  93. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/datasets.md +0 -0
  94. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/io.md +0 -0
  95. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/models.md +0 -0
  96. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/operations.md +0 -0
  97. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/testing.md +0 -0
  98. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/transformations.md +0 -0
  99. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api/transformations_utils.md +0 -0
  100. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/api.md +0 -0
  101. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/changelog.md +0 -0
  102. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/conf.py +0 -0
  103. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/contributing.md +0 -0
  104. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/design_doc.md +0 -0
  105. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/extensions/typed_returns.py +0 -0
  106. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/glossary.md +0 -0
  107. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/installation.md +0 -0
  108. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/references.bib +0 -0
  109. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/references.md +0 -0
  110. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/docs/user_guide.md +0 -0
  111. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/__init__.py +0 -0
  112. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/__main__.py +0 -0
  113. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_bridges/__init__.py +0 -0
  114. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/__init__.py +0 -0
  115. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/_deepcopy.py +0 -0
  116. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/data_extent.py +0 -0
  117. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/__init__.py +0 -0
  118. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/_utils.py +0 -0
  119. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/aggregate.py +0 -0
  120. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/map.py +0 -0
  121. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/rasterize.py +0 -0
  122. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/operations/vectorize.py +0 -0
  123. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_core/query/__init__.py +0 -0
  124. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_docs.py +0 -0
  125. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/__init__.py +0 -0
  126. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/format.py +0 -0
  127. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_io/io_zarr.py +0 -0
  128. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_logging.py +0 -0
  129. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/_utils.py +0 -0
  130. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/config.py +0 -0
  131. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/io/__init__.py +0 -0
  132. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/models/_accessor.py +0 -0
  133. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/models/pyramids_utils.py +0 -0
  134. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/testing.py +0 -0
  135. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/__init__.py +0 -0
  136. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/_utils.py +0 -0
  137. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/__init__.py +0 -0
  138. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/_utils.py +0 -0
  139. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/ngff_coordinate_system.py +0 -0
  140. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/ngff_transformations.py +0 -0
  141. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/operations.py +0 -0
  142. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/src/spatialdata/transformations/transformations.py +0 -0
  143. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/__init__.py +0 -0
  144. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/__init__.py +0 -0
  145. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/__init__.py +0 -0
  146. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_aggregations.py +0 -0
  147. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_map.py +0 -0
  148. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/operations/test_rasterize.py +0 -0
  149. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/query/__init__.py +0 -0
  150. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/test_centroids.py +0 -0
  151. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/test_deepcopy.py +0 -0
  152. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/core/test_validation.py +0 -0
  153. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/data/multipolygon.json +0 -0
  154. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/data/points.json +0 -0
  155. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/data/polygon.json +0 -0
  156. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/dataloader/__init__.py +0 -0
  157. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/dataloader/test_datasets.py +0 -0
  158. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/datasets/__init__.py +0 -0
  159. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/datasets/test_datasets.py +0 -0
  160. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/io/__init__.py +0 -0
  161. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/models/__init__.py +0 -0
  162. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/models/test_accessor.py +0 -0
  163. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/models/test_chunks_utils.py +0 -0
  164. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/models/test_pyramids_utils.py +0 -0
  165. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/test_init.py +0 -0
  166. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/__init__.py +0 -0
  167. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/ngff/__init__.py +0 -0
  168. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/ngff/conftest.py +0 -0
  169. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/ngff/test_ngff_coordinate_system.py +0 -0
  170. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/ngff/test_ngff_transformations.py +0 -0
  171. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/test_transformations.py +0 -0
  172. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/transformations/test_transformations_utils.py +0 -0
  173. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/utils/__init__.py +0 -0
  174. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/utils/test_element_utils.py +0 -0
  175. {spatialdata-0.7.3a1 → spatialdata-0.8.0}/tests/utils/test_testing.py +0 -0
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: memray
3
+ description: Profile the memory usage of a Python script using memray and visualize a temporal flamegraph in the browser. Use when the user wants to investigate memory consumption, find leaks, or understand allocation patterns.
4
+ compatibility: Requires the pixi profiling environment (pixi run -e profiling). Supports Linux and macOS.
5
+ allowed-tools: Bash(pixi run -e profiling memray-run:*) Bash(pixi run -e profiling memray-flame:*) Bash(open:*) Bash(xdg-open:*) Bash(python -m webbrowser:*)
6
+ ---
7
+
8
+ ## Steps
9
+
10
+ 1. Ask the user which script to profile (full or relative path).
11
+
12
+ 2. Run the script under memray:
13
+
14
+ ```bash
15
+ pixi run -e profiling memray-run script.py
16
+ ```
17
+
18
+ This produces a binary file named `memray-script.py.<pid>.bin` in the current directory.
19
+
20
+ 3. Generate the flamegraph HTML report from the `.bin` file:
21
+
22
+ ```bash
23
+ pixi run -e profiling memray-flame memray-script.py.<pid>.bin
24
+ ```
25
+
26
+ Replace `<pid>` with the actual PID shown in the filename. This writes `memray-flamegraph-script.py.<pid>.html`.
27
+
28
+ 4. Open the report in the browser:
29
+ - macOS: `open memray-flamegraph-script.py.<pid>.html`
30
+ - Linux: `xdg-open memray-flamegraph-script.py.<pid>.html`
31
+ - Either: `python -m webbrowser memray-flamegraph-script.py.<pid>.html`
32
+
33
+ ## Notes
34
+
35
+ - The `--temporal` flag (included in `memray-flame`) shows memory over time, not just peak — use this to spot leaks and allocation bursts.
36
+ - To find the `.bin` file if unsure of the name: `ls memray-*.bin`
37
+ - To compare runs, save the previous report: `cp memray-flamegraph-script.py.<pid>.html memray-flamegraph-before.html`
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: profimp
3
+ description: Profile Python import time using profimp and open a waterfall HTML report. Use when investigating slow startup or wanting to identify which imports are most expensive.
4
+ compatibility: Requires profimp (available as a pixi dependency). macOS or Linux.
5
+ allowed-tools: Bash(profimp:*) Bash(open:*) Bash(xdg-open:*) Bash(python -m webbrowser:*)
6
+ ---
7
+
8
+ ## Steps
9
+
10
+ 1. Ask what to profile. Suggest common patterns for this repo:
11
+ - `import spatialdata`
12
+ - `from spatialdata import SpatialData`
13
+ - `from spatialdata_io import xenium`
14
+
15
+ 2. Run:
16
+
17
+ ```bash
18
+ profimp --html "<import_stmt>" > /tmp/profimp.html
19
+ ```
20
+
21
+ 3. Open the report:
22
+ - macOS: `open /tmp/profimp.html`
23
+ - Linux: `xdg-open /tmp/profimp.html`
24
+ - Either: `python -m webbrowser /tmp/profimp.html`
25
+
26
+ ## Notes
27
+
28
+ - The report is a waterfall chart showing every sub-import and its timing.
29
+ - To compare before/after: `cp /tmp/profimp.html /tmp/profimp-before.html` before re-running.
30
+ - `pixi run python -m profimp` also works if `profimp` is not on PATH.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: pyspy
3
+ description: Profile the execution time of a Python script using py-spy and visualize the result with speedscope. Use when the user wants to benchmark performance, find slow code paths, or profile CPU time.
4
+ compatibility: Requires the pixi profiling environment (pixi run -e profiling). Speedscope must be installed separately (npm install -g speedscope). sudo is required on macOS.
5
+ allowed-tools: Bash(pixi run -e profiling pyspy:*) Bash(pixi run -e profiling speedscope:*) Bash(sudo pixi run -e profiling pyspy:*)
6
+ ---
7
+
8
+ ## Steps
9
+
10
+ 1. Ask the user which script to profile (full or relative path).
11
+
12
+ 2. Run py-spy to record the profile. The output is always written to `profile.speedscope.json` in the current directory.
13
+
14
+ **Linux** (no sudo needed):
15
+
16
+ ```bash
17
+ pixi run -e profiling pyspy script.py
18
+ ```
19
+
20
+ **macOS** (sudo required — py-spy needs to attach to the process):
21
+
22
+ ```bash
23
+ sudo pixi run -e profiling pyspy script.py
24
+ ```
25
+
26
+ If sudo fails to find the pixi environment, use absolute paths:
27
+
28
+ ```bash
29
+ sudo /path/to/.pixi/envs/profiling/bin/py-spy record --gil \
30
+ -o profile.speedscope.json --format speedscope \
31
+ -- /path/to/.pixi/envs/profiling/bin/python script.py
32
+ ```
33
+
34
+ 3. Open the result in speedscope:
35
+ ```bash
36
+ pixi run -e profiling speedscope
37
+ ```
38
+ This opens `profile.speedscope.json` in the browser via the local speedscope CLI.
39
+
40
+ ## Notes
41
+
42
+ - If the speedscope view is blank, switch threads using the thread selector in the top-right corner.
43
+ - To save a profile before overwriting: `cp profile.speedscope.json profile-before.speedscope.json`
44
+ - `--gil` records only time when the GIL is held (Python-level CPU time). Drop it to include C extension time.
45
+ - speedscope must be installed globally: `npm install -g speedscope`
@@ -19,12 +19,13 @@ jobs:
19
19
  fail-fast: false
20
20
  matrix:
21
21
  include:
22
- - {os: windows-latest, python: "3.11", dask-version: "2025.12.0", name: "Dask 2025.12.0"}
23
- - {os: windows-latest, python: "3.13", dask-version: "latest", name: "Dask latest"}
24
- - {os: ubuntu-latest, python: "3.11", dask-version: "latest", name: "Dask latest"}
25
- - {os: ubuntu-latest, python: "3.13", dask-version: "latest", name: "Dask latest"}
26
- - {os: macos-latest, python: "3.11", dask-version: "latest", name: "Dask latest"}
27
- - {os: macos-latest, python: "3.13", prerelease: "allow", name: "Python 3.13 (pre-release)"}
22
+ - {os: windows-latest, python: "3.12", dask-version: "2026.3.0", name: "min dask"}
23
+ - {os: windows-latest, python: "3.14", dask-version: "latest"}
24
+ - {os: ubuntu-latest, python: "3.12", dask-version: "latest"}
25
+ - {os: ubuntu-latest, python: "3.13", dask-version: "latest"}
26
+ - {os: ubuntu-latest, python: "3.14", dask-version: "latest"}
27
+ - {os: macos-latest, python: "3.12", dask-version: "latest"}
28
+ - {os: macos-latest, python: "3.14", prerelease: "allow", name: "prerelease"}
28
29
  env:
29
30
  OS: ${{ matrix.os }}
30
31
  PYTHON: ${{ matrix.python }}
@@ -41,7 +42,6 @@ jobs:
41
42
  - name: Install dependencies
42
43
  run: |
43
44
  if [[ "${PRERELEASE}" == "allow" ]]; then
44
- sed -i '' 's/requires-python.*//' pyproject.toml # otherwise uv complains that anndata requires python>=3.12 and we only do >=3.11 😱
45
45
  uv add git+https://github.com/scverse/anndata.git
46
46
  uv add pandas>=3.dev0
47
47
  fi
@@ -59,7 +59,7 @@ jobs:
59
59
  PLATFORM: ${{ matrix.os }}
60
60
  DISPLAY: :42
61
61
  run: |
62
- uv run pytest --cov --color=yes --cov-report=xml
62
+ uv run pytest --cov --color=yes --cov-report=xml -n auto --dist worksteal
63
63
  - name: Upload coverage to Codecov
64
64
  uses: codecov/codecov-action@v5
65
65
  with:
@@ -46,11 +46,18 @@ spatialdata-sandbox
46
46
  # version file
47
47
  _version.py
48
48
 
49
- # other
50
- node_modules/
49
+ # agents configurations
50
+ .claude/settings.local.json
51
51
 
52
+ # benchmarking and profiling
52
53
  .asv/
54
+ profile.speedscope.json
55
+
56
+ # other
57
+ node_modules/
53
58
 
54
59
  .mypy_cache
55
60
  .ruff_cache
56
61
  uv.lock
62
+ pixi.lock
63
+
@@ -1,5 +1,5 @@
1
1
  [mypy]
2
- python_version = 3.11
2
+ python_version = 3.12
3
3
 
4
4
  ignore_errors = False
5
5
  warn_redundant_casts = True
@@ -9,18 +9,18 @@ ci:
9
9
  skip: []
10
10
  repos:
11
11
  - repo: https://github.com/rbubley/mirrors-prettier
12
- rev: v3.8.1
12
+ rev: v3.8.4
13
13
  hooks:
14
14
  - id: prettier
15
15
  exclude: ^.github/workflows/test.yaml
16
16
  - repo: https://github.com/pre-commit/mirrors-mypy
17
- rev: v1.19.1
17
+ rev: v2.1.0
18
18
  hooks:
19
19
  - id: mypy
20
20
  additional_dependencies: [numpy, types-requests]
21
21
  exclude: tests/|docs/
22
22
  - repo: https://github.com/astral-sh/ruff-pre-commit
23
- rev: v0.15.2
23
+ rev: v0.15.18
24
24
  hooks:
25
25
  - id: ruff
26
26
  args: [--fix, --exit-non-zero-on-fix]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatialdata
3
- Version: 0.7.3a1
3
+ Version: 0.8.0
4
4
  Summary: Spatial data format.
5
5
  Project-URL: Documentation, https://spatialdata.scverse.org/en/latest
6
6
  Project-URL: Source, https://github.com/scverse/spatialdata.git
@@ -37,21 +37,21 @@ License: BSD 3-Clause License
37
37
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
38
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
39
  License-File: LICENSE
40
- Requires-Python: >=3.11
41
- Requires-Dist: anndata>=0.12.10
40
+ Requires-Python: >=3.12
41
+ Requires-Dist: anndata>=0.9.1
42
42
  Requires-Dist: annsel>=0.1.2
43
43
  Requires-Dist: click
44
44
  Requires-Dist: dask-image
45
- Requires-Dist: dask<2026.1.2,>=2025.12.0
45
+ Requires-Dist: dask>=2026.3.0
46
46
  Requires-Dist: datashader
47
- Requires-Dist: distributed<2026.1.2
47
+ Requires-Dist: distributed>=2026.3.0
48
48
  Requires-Dist: fsspec[http,s3]
49
49
  Requires-Dist: geopandas>=0.14
50
50
  Requires-Dist: multiscale-spatial-image==2.0.3
51
51
  Requires-Dist: networkx
52
52
  Requires-Dist: numba>=0.55.0
53
53
  Requires-Dist: numpy
54
- Requires-Dist: ome-zarr>=0.14.0
54
+ Requires-Dist: ome-zarr>=0.16.0
55
55
  Requires-Dist: pandas
56
56
  Requires-Dist: pooch
57
57
  Requires-Dist: pyarrow
@@ -82,7 +82,7 @@ Description-Content-Type: text/markdown
82
82
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)
83
83
  [![codecov](https://codecov.io/gh/scverse/spatialdata/branch/main/graph/badge.svg?token=X19DRSIMCU)](https://codecov.io/gh/scverse/spatialdata)
84
84
  [![documentation badge](https://readthedocs.org/projects/scverse-spatialdata/badge/?version=latest)](https://spatialdata.scverse.org/en/latest/)
85
- [![DOI](https://zenodo.org/badge/487366481.svg)](https://zenodo.org/badge/latestdoi/487366481)
85
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20056407.svg)](https://doi.org/10.5281/zenodo.20056407)
86
86
  [![Downloads](https://static.pepy.tech/badge/spatialdata)](https://pepy.tech/project/spatialdata)
87
87
  [![Release](https://github.com/scverse/spatialdata/actions/workflows/release.yaml/badge.svg?event=release)](https://github.com/scverse/spatialdata/actions/workflows/release.yaml)
88
88
  [![Documentation][badge-pypi]][link-pypi]
@@ -91,11 +91,15 @@ Description-Content-Type: text/markdown
91
91
  [badge-pypi]: https://badge.fury.io/py/spatialdata.svg
92
92
  [link-pypi]: https://pypi.org/project/spatialdata/
93
93
 
94
- SpatialData is a data framework that comprises a FAIR storage format and a collection of python libraries for performant access, alignment, and processing of uni- and multi-modal spatial omics datasets. This repository contains the core spatialdata library. See the links below to learn more about other packages in the SpatialData ecosystem.
94
+ SpatialData is a data framework, a schema and a serialization format for uni- and multi-modal spatial omics datasets. It is supported by a growing suite of software libraries for performant access, manipulation and analysis in multiple languages, including Python, R and JavaScript.
95
+
96
+ This repository contains the core spatialdata library. See the links below to learn more about other packages in the SpatialData ecosystem.
95
97
 
96
98
  - [spatialdata-io](https://github.com/scverse/spatialdata-io): load data from common spatial omics technologies into spatialdata.
97
99
  - [spatialdata-plot](https://github.com/scverse/spatialdata-plot): Static plotting library for spatialdata.
98
100
  - [napari-spatialdata](https://github.com/scverse/napari-spatialdata): napari plugin for interactive exploration and annotation of spatial data.
101
+ - [SpatialData (R)](https://github.com/HelenaLC/SpatialData): R implementation of the SpatialData framework.
102
+ - [SpatialData.js](https://github.com/Taylor-CCB-Group/SpatialData.js): JavaScript/TypeScript implementation of the SpatialData framework.
99
103
 
100
104
  [//]: # "numfocus-fiscal-sponsor-attribution"
101
105
 
@@ -6,7 +6,7 @@
6
6
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)
7
7
  [![codecov](https://codecov.io/gh/scverse/spatialdata/branch/main/graph/badge.svg?token=X19DRSIMCU)](https://codecov.io/gh/scverse/spatialdata)
8
8
  [![documentation badge](https://readthedocs.org/projects/scverse-spatialdata/badge/?version=latest)](https://spatialdata.scverse.org/en/latest/)
9
- [![DOI](https://zenodo.org/badge/487366481.svg)](https://zenodo.org/badge/latestdoi/487366481)
9
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20056407.svg)](https://doi.org/10.5281/zenodo.20056407)
10
10
  [![Downloads](https://static.pepy.tech/badge/spatialdata)](https://pepy.tech/project/spatialdata)
11
11
  [![Release](https://github.com/scverse/spatialdata/actions/workflows/release.yaml/badge.svg?event=release)](https://github.com/scverse/spatialdata/actions/workflows/release.yaml)
12
12
  [![Documentation][badge-pypi]][link-pypi]
@@ -15,11 +15,15 @@
15
15
  [badge-pypi]: https://badge.fury.io/py/spatialdata.svg
16
16
  [link-pypi]: https://pypi.org/project/spatialdata/
17
17
 
18
- SpatialData is a data framework that comprises a FAIR storage format and a collection of python libraries for performant access, alignment, and processing of uni- and multi-modal spatial omics datasets. This repository contains the core spatialdata library. See the links below to learn more about other packages in the SpatialData ecosystem.
18
+ SpatialData is a data framework, a schema and a serialization format for uni- and multi-modal spatial omics datasets. It is supported by a growing suite of software libraries for performant access, manipulation and analysis in multiple languages, including Python, R and JavaScript.
19
+
20
+ This repository contains the core spatialdata library. See the links below to learn more about other packages in the SpatialData ecosystem.
19
21
 
20
22
  - [spatialdata-io](https://github.com/scverse/spatialdata-io): load data from common spatial omics technologies into spatialdata.
21
23
  - [spatialdata-plot](https://github.com/scverse/spatialdata-plot): Static plotting library for spatialdata.
22
24
  - [napari-spatialdata](https://github.com/scverse/napari-spatialdata): napari plugin for interactive exploration and annotation of spatial data.
25
+ - [SpatialData (R)](https://github.com/HelenaLC/SpatialData): R implementation of the SpatialData framework.
26
+ - [SpatialData.js](https://github.com/Taylor-CCB-Group/SpatialData.js): JavaScript/TypeScript implementation of the SpatialData framework.
23
27
 
24
28
  [//]: # "numfocus-fiscal-sponsor-attribution"
25
29
 
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.8.0'
22
+ __version_tuple__ = version_tuple = (0, 8, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -65,6 +65,32 @@ git checkout - && git stash pop
65
65
  asv compare main HEAD
66
66
  ```
67
67
 
68
+ ### Dataloader benchmarks
69
+
70
+ Dataloader benchmarks live in `benchmarks/benchmark_dataloader.py`. They use a synthetic in-memory `SpatialData` (2048×2048 image, 500 circle regions) and compute two metrics:
71
+
72
+ - `time_init` — constructing `ImageTilesDataset` (includes bounding-box pre-computation).
73
+ - `time_fetch` — iterating over all 500 tiles once (pure `__getitem__` calls, no `DataLoader` overhead).
74
+
75
+ Run both in your current environment:
76
+
77
+ ```bash
78
+ asv run --python=same --show-stderr -b TimeDataloader
79
+ ```
80
+
81
+ Or a single method:
82
+
83
+ ```bash
84
+ asv run --python=same --show-stderr -b TimeDataloader.time_init
85
+ asv run --python=same --show-stderr -b TimeDataloader.time_fetch
86
+ ```
87
+
88
+ Compare against `main` in one shot:
89
+
90
+ ```bash
91
+ asv continuous --show-stderr -v -b TimeDataloader main HEAD
92
+ ```
93
+
68
94
  ### Querying benchmarks
69
95
 
70
96
  Querying using a bounding box without a spatial index is highly impacted by large amounts of points (transcripts), more than table rows (cells).
@@ -0,0 +1,75 @@
1
+ # type: ignore
2
+ """Benchmarks for ImageTilesDataset: init time and iteration time."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import anndata as ad
7
+ import geopandas as gpd
8
+ import numpy as np
9
+ import pandas as pd
10
+ from shapely.geometry import Point
11
+
12
+ import spatialdata as sd
13
+ from spatialdata.dataloader import ImageTilesDataset
14
+ from spatialdata.models import Image2DModel, ShapesModel, TableModel
15
+ from spatialdata.transformations import Identity
16
+
17
+ _RNG = np.random.default_rng(42)
18
+
19
+ _IMAGE_SIZE = 2048
20
+ _N_CIRCLES = 500
21
+ _N_CHANNELS = 3
22
+
23
+ _DATASET_KWARGS = {
24
+ "regions_to_images": {"circles": "image"},
25
+ "regions_to_coordinate_systems": {"circles": "global"},
26
+ "table_name": "table",
27
+ "return_annotations": "instance_id",
28
+ }
29
+
30
+
31
+ def _make_sdata() -> sd.SpatialData:
32
+ img_data = _RNG.integers(0, 256, size=(_N_CHANNELS, _IMAGE_SIZE, _IMAGE_SIZE), dtype=np.uint8).astype(np.float32)
33
+ image = Image2DModel.parse(img_data, dims=["c", "y", "x"], transformations={"global": Identity()})
34
+
35
+ radius = 32.0
36
+ cx = _RNG.uniform(radius, _IMAGE_SIZE - radius, size=_N_CIRCLES)
37
+ cy = _RNG.uniform(radius, _IMAGE_SIZE - radius, size=_N_CIRCLES)
38
+ geom = gpd.GeoDataFrame({"geometry": [Point(x, y) for x, y in zip(cx, cy, strict=True)]})
39
+ geom["radius"] = radius
40
+ circles = ShapesModel.parse(geom, transformations={"global": Identity()})
41
+
42
+ table = ad.AnnData(
43
+ _RNG.random((_N_CIRCLES, 10)).astype(np.float32),
44
+ obs=pd.DataFrame(
45
+ {
46
+ "region": pd.Categorical(["circles"] * _N_CIRCLES),
47
+ "instance_id": np.arange(_N_CIRCLES, dtype=np.int64),
48
+ },
49
+ index=[str(i) for i in range(_N_CIRCLES)],
50
+ ),
51
+ )
52
+ table = TableModel.parse(table, region="circles", region_key="region", instance_key="instance_id")
53
+
54
+ return sd.SpatialData(images={"image": image}, shapes={"circles": circles}, tables={"table": table})
55
+
56
+
57
+ class TimeDataloader:
58
+ """Time ImageTilesDataset construction and tile iteration."""
59
+
60
+ def setup(self):
61
+ self.sdata = _make_sdata()
62
+ self.ds = ImageTilesDataset(sdata=self.sdata, **_DATASET_KWARGS)
63
+
64
+ def teardown(self):
65
+ del self.ds
66
+ del self.sdata
67
+
68
+ def time_init(self):
69
+ """Time constructing ImageTilesDataset (bounding-box pre-computation)."""
70
+ ImageTilesDataset(sdata=self.sdata, **_DATASET_KWARGS)
71
+
72
+ def time_fetch(self):
73
+ """Time iterating over every tile once."""
74
+ for i in range(len(self.ds)):
75
+ _ = self.ds[i]
@@ -4,6 +4,7 @@
4
4
  .. currentmodule:: spatialdata.models
5
5
 
6
6
  .. autofunction:: get_model
7
+ .. autofunction:: validate_element
7
8
  .. autodata:: SpatialElement
8
9
  .. autofunction:: get_axes_names
9
10
  .. autofunction:: get_spatial_axes
@@ -6,11 +6,15 @@
6
6
 
7
7
  # An open and universal framework for processing spatial omics data.
8
8
 
9
- SpatialData is a data framework that comprises a FAIR storage format and a collection of python libraries for performant access, alignment, and processing of uni- and multi-modal spatial omics datasets. This page provides documentation on how to install, use, and extend the core `spatialdata` library. See the links below to learn more about other packages in the SpatialData ecosystem.
9
+ SpatialData is a data framework, a schema and a serialization format for uni- and multi-modal spatial omics datasets. It is supported by a growing suite of software libraries for performant access, manipulation and analysis in multiple languages, including Python, R and JavaScript.
10
+
11
+ This page provides documentation on how to install, use, and extend the core `spatialdata` library. See the links below to learn more about other packages in the SpatialData ecosystem.
10
12
 
11
13
  - `spatialdata-io`: load data from common spatial omics technologies into `spatialdata` ([repository][spatialdata-io-repo], [documentation][spatialdata-io-docs]).
12
14
  - `spatialdata-plot`: Static plotting library for `spatialdata` ([repository][spatialdata-plot-repo], [documentation][spatialdata-plot-docs]).
13
15
  - `napari-spatialdata-repo`: napari plugin for interactive exploration and annotation of `spatialdata` ([repository][napari-spatialdata-repo], [documentation][napari-spatialdata-docs]).
16
+ - `SpatialData` (R): R implementation of the SpatialData framework ([repository][spatialdata-r-repo]).
17
+ - `SpatialData.js`: JavaScript/TypeScript implementation of the SpatialData framework ([repository][spatialdata-js-repo]).
14
18
 
15
19
  Please see our publication {cite}`marconatoSpatialDataOpenUniversal2024` for citation and to learn more.
16
20
 
@@ -119,3 +123,5 @@ references.md
119
123
  [napari-spatialdata-docs]: https://spatialdata.scverse.org/projects/napari/en/stable/notebooks/spatialdata.html
120
124
  [spatialdata-io-docs]: https://spatialdata.scverse.org/projects/io/en/stable/
121
125
  [spatialdata-plot-docs]: https://spatialdata.scverse.org/projects/plot/en/stable/api.html
126
+ [spatialdata-r-repo]: https://github.com/HelenaLC/SpatialData
127
+ [spatialdata-js-repo]: https://github.com/Taylor-CCB-Group/SpatialData.js
@@ -15,19 +15,19 @@ maintainers = [
15
15
  urls.Documentation = "https://spatialdata.scverse.org/en/latest"
16
16
  urls.Source = "https://github.com/scverse/spatialdata.git"
17
17
  urls.Home-page = "https://github.com/scverse/spatialdata.git"
18
- requires-python = ">=3.11"
18
+ requires-python = ">=3.12"
19
19
  dynamic= [
20
20
  "version" # allow version to be set by git tags
21
21
  ]
22
22
  license = {file = "LICENSE"}
23
23
  readme = "README.md"
24
24
  dependencies = [
25
- "anndata>=0.12.10",
25
+ "anndata>=0.9.1",
26
26
  "annsel>=0.1.2",
27
27
  "click",
28
28
  "dask-image",
29
- "dask>=2025.12.0,<2026.1.2",
30
- "distributed<2026.1.2",
29
+ "dask>=2026.3.0",
30
+ "distributed>=2026.3.0",
31
31
  "datashader",
32
32
  "fsspec[s3,http]",
33
33
  "geopandas>=0.14",
@@ -35,7 +35,7 @@ dependencies = [
35
35
  "networkx",
36
36
  "numba>=0.55.0",
37
37
  "numpy",
38
- "ome_zarr>=0.14.0",
38
+ "ome_zarr>=0.16.0",
39
39
  "pandas",
40
40
  "pooch",
41
41
  "pyarrow",
@@ -69,6 +69,7 @@ test = [
69
69
  "pytest",
70
70
  "pytest-cov",
71
71
  "pytest-mock",
72
+ "pytest-xdist",
72
73
  "torch",
73
74
  ]
74
75
  docs = [
@@ -102,6 +103,7 @@ strict = true
102
103
  addopts = [
103
104
  "--import-mode=importlib", # allow using test files with same name
104
105
  "-s", # print output from tests
106
+ "-p no:napari", # napari registers a pytest plugin via its entry point; disable it here since spatialdata tests don't need it
105
107
  ]
106
108
  # These are all markers coming from xarray, dask or anndata. Added here to silence warnings.
107
109
  markers = [
@@ -143,7 +145,7 @@ exclude = [
143
145
 
144
146
  ]
145
147
  line-length = 120
146
- target-version = "py311"
148
+ target-version = "py312"
147
149
 
148
150
  [tool.ruff.lint]
149
151
  ignore = [
@@ -208,3 +210,37 @@ convention = "numpy"
208
210
 
209
211
  # pyupgrade typing rewrite TODO: remove at some point from per-file ignore
210
212
  # "UP006", "UP007"
213
+
214
+ [tool.pixi.workspace]
215
+ channels = ["conda-forge"]
216
+ platforms = ["osx-arm64", "linux-64"]
217
+
218
+ [tool.pixi.dependencies]
219
+ proj = "*"
220
+
221
+ [tool.pixi.tasks]
222
+ test = "pytest"
223
+ test-parallel = "pytest -n auto --dist worksteal"
224
+ pre-commit = "pre-commit run"
225
+ pre-commit-all = "pre-commit run --all-files"
226
+
227
+ [tool.pixi.feature.profiling.dependencies]
228
+ py-spy = "*"
229
+
230
+ [tool.pixi.feature.profiling.pypi-dependencies]
231
+ spatialdata = { path = ".", editable = true }
232
+ profimp = "*"
233
+ memray = "*"
234
+
235
+ [tool.pixi.feature.profiling.tasks]
236
+ # Usage: pixi run -e profiling pyspy script.py (prefix with sudo on macOS)
237
+ pyspy = "py-spy record --gil -o profile.speedscope.json --format speedscope -- python"
238
+ # Usage: pixi run -e profiling speedscope
239
+ speedscope = "npx --yes speedscope profile.speedscope.json"
240
+ # Usage: pixi run -e profiling memray-run script.py
241
+ memray-run = "memray run"
242
+ # Usage: pixi run -e profiling memray-flame memray-script.py.<pid>.bin
243
+ memray-flame = "memray flamegraph --temporal"
244
+
245
+ [tool.pixi.environments]
246
+ profiling = { features = ["profiling"], solve-group = "default" }
@@ -21,7 +21,6 @@ from spatialdata.models import (
21
21
  PointsModel,
22
22
  ShapesModel,
23
23
  TableModel,
24
- get_axes_names,
25
24
  get_model,
26
25
  )
27
26
 
@@ -50,6 +49,7 @@ class Elements(UserDict[str, T]):
50
49
  raise KeyError(*e.args) from e
51
50
 
52
51
  def __setitem__(self, key: str, value: T) -> None:
52
+ # note that each __setitem__ in the subclasses calls get_model(), which performs data validation
53
53
  self._add_shared_key(key)
54
54
  super().__setitem__(key, value)
55
55
 
@@ -72,15 +72,7 @@ class Images(Elements[DataArray | DataTree]):
72
72
  schema = get_model(value)
73
73
  if schema not in (Image2DModel, Image3DModel):
74
74
  raise TypeError(f"Unknown element type with schema: {schema!r}.")
75
- ndim = len(get_axes_names(value))
76
- if ndim == 3:
77
- Image2DModel.validate(value)
78
- super().__setitem__(key, value)
79
- elif ndim == 4:
80
- Image3DModel.validate(value)
81
- super().__setitem__(key, value)
82
- else:
83
- NotImplementedError("TODO: implement for ndim > 4.")
75
+ super().__setitem__(key, value)
84
76
 
85
77
 
86
78
  class Labels(Elements[DataArray | DataTree]):
@@ -89,15 +81,7 @@ class Labels(Elements[DataArray | DataTree]):
89
81
  schema = get_model(value)
90
82
  if schema not in (Labels2DModel, Labels3DModel):
91
83
  raise TypeError(f"Unknown element type with schema: {schema!r}.")
92
- ndim = len(get_axes_names(value))
93
- if ndim == 2:
94
- Labels2DModel.validate(value)
95
- super().__setitem__(key, value)
96
- elif ndim == 3:
97
- Labels3DModel.validate(value)
98
- super().__setitem__(key, value)
99
- else:
100
- NotImplementedError("TODO: implement for ndim > 3.")
84
+ super().__setitem__(key, value)
101
85
 
102
86
 
103
87
  class Shapes(Elements[GeoDataFrame]):
@@ -106,7 +90,6 @@ class Shapes(Elements[GeoDataFrame]):
106
90
  schema = get_model(value)
107
91
  if schema != ShapesModel:
108
92
  raise TypeError(f"Unknown element type with schema: {schema!r}.")
109
- ShapesModel.validate(value)
110
93
  super().__setitem__(key, value)
111
94
 
112
95
 
@@ -116,7 +99,6 @@ class Points(Elements[DaskDataFrame]):
116
99
  schema = get_model(value)
117
100
  if schema != PointsModel:
118
101
  raise TypeError(f"Unknown element type with schema: {schema!r}.")
119
- PointsModel.validate(value)
120
102
  super().__setitem__(key, value)
121
103
 
122
104
 
@@ -126,5 +108,4 @@ class Tables(Elements[AnnData]):
126
108
  schema = get_model(value)
127
109
  if schema != TableModel:
128
110
  raise TypeError(f"Unknown element type with schema: {schema!r}.")
129
- TableModel.validate(value)
130
111
  super().__setitem__(key, value)
@@ -158,9 +158,9 @@ def sanitize_table(data: AnnData, inplace: bool = True) -> AnnData | None:
158
158
  # Handle other attributes
159
159
  for attr in ("obsm", "obsp", "varm", "varp", "uns", "layers"):
160
160
  d = getattr(sanitized, attr)
161
- new_keys = {old: get_unique_name(old, attr) for old in d}
162
- # Create new dictionary with sanitized keys
163
- new_dict = {new_keys[old]: value for old, value in d.items()}
161
+ # None is a valid key in layers (anndata >= 0.13: represents X); skip sanitizing it
162
+ new_keys = {old: get_unique_name(old, attr) for old in d if old is not None}
163
+ new_dict = {(new_keys[old] if old is not None else old): value for old, value in d.items()}
164
164
  setattr(sanitized, attr, new_dict)
165
165
 
166
166
  return None if inplace else sanitized