Repository navigation
Description
Note
This whole message is AI-generated. The issue was automatically discovered and reported by an AI agent (Claude) during an autonomous bug hunt on the spatialdata code base. It has not been verified or triaged by a human yet; the needs: triage label is set so that a maintainer can confirm it. The reproduction script below was executed by the agent in an isolated environment (see Environment) and its output is pasted verbatim.
Summary
Each item was confirmed with a script; the repro below demonstrates items 1–8.
- Empty
GeoDataFrame→IndexErrorinShapesModel.parseandget_axes_names(.iloc[0]) instead of theValueError("Column geometry is empty")thatvalidatewould raise. TableModel.parse(region=("a", "b"))(tuple) fails with the misleadingadata.obs[region] values do not match with region values;region=pd.Index([...])fails withTypeError: unhashable type: 'Index'. Onlylist/np.ndarrayare normalised.RasterSchema.parse(chunks=<float>)crashes withValueError: not enough values to unpack({dim: chunks for index, dim in data.dims}iterates dimension names).RasterSchema.parse(DataArray)mutates its input: a numpy-backedDataArrayhas its.datareplaced by a dask array in place.PointsModel.validaterejects pandas nullable / pyarrow numeric coordinate dtypes (Int64,float64[pyarrow]) while accepting every numpy int/float — usepd.api.types.is_numeric_dtype.get_element_instances(shapes_or_points, return_background=True)→TypeError: _() got an unexpected keyword argument(the GeoDataFrame/DaskDataFrame overloads lack the keyword the generic signature advertises).set_table_annotates_spatialelement(region=pd.Series([...]))— the type hint allows a Series, but the Series object is stored inuns["spatialdata_attrs"]["region"];TableModel.validatethen fails withunhashable type: 'Series'and the table cannot be written.filter_by_table_query(table_name, element_names=[...])raisesKeyError: 't'when no table row annotates the requested elements (subset(filter_tables=True)drops the empty table beforesdata_subset.tables[table_name]).
Not in the script:
- CLI:
python -m spatialdata peek <store> tablesilently drops the tables — the clickChoiceoffers"table"(singular) butread_zarrexpects"tables", andtablesis rejected by click. TableModel.parseuniqueness check computesgroupby(region).nunique()over all obs columns instead of only the instance key (10–100× slower; 0.35 s vs 0.02 s for 1 M rows × 54 columns). Usegrouped[instance_key].nunique().relabel_sequentialbreaks when the internal lookup array spans several dask chunks (max_label> ~16 M with the default chunk size):ValueError: Dimension 1 has N blocks, adjust_chunks specified with 1 blocks.rechunk(-1)the lookup array.polygon_query(circles, clip=True)returns Polygon geometries but keeps theradiuscolumn.to_polygons(circles)buffers row-by-row withDataFrame.apply(axis=1)(2.1 s for 167 k Xenium circles) whereasshapely.buffer(geoms, radii, quad_segs=...)takes 0.58 s with identical areas.map_rasterdocstring: changing dimensionality needsdrop_axis/new_axis(forwarded via**kwargs) in addition todims; with onlydims=("y", "x")the call fails with number of dimensions of the output data (3) differs....get_transformation_between_landmarksdocstring example usesPointsModel(points_moving)instead ofPointsModel.parse(...);_get_current_output_axescontains a dead statementset(transformation.map_axis.keys()).- Compressor level docs inconsistent:
_validate_compressor_argsmessages say 'between 1 and 9' in two places and 'between 0 and 9' in another; level 0 is accepted. - Docstring/signature mismatches:
rasterize_binsdocumentsreturn_regions_as_labels(parameter:return_region_as_labels);SpatialData.subsetdocumentsfilter_table(parameter:filter_tables);validate_axesdocumentsaxis(parameter:axes);get_extent's generic docstring lists its parameters under 'Returns';get_transformation_between_coordinate_systemsdoes not documentsdata/intermediate_coordinate_systems; the points↔geopandas converters do not documentsuppress_z_warning. - Forward compatibility:
Pandas4Warning: The copy keyword is deprecatedfromtable.obs.rename(columns=..., copy=False)inconcatenate._concatenate_tables(will raise in pandas 4). ShapesModel.validateinspects only the first geometry, so[Polygon, Point]without a radius column is accepted;validate_shapes_not_mixed_typesexists but is opt-in.- Table filtering via left join computes
da.uniqueon full-resolution labels insubset(),filter_by_coordinate_system()and spatial queries (0.1 s per 3648×5472 labels, 1.8 s forfilter_by_coordinate_systemon the 30-labels CosMx dataset); forfilter_by_coordinate_systema name-based filter would be O(n_rows). (See also Bounding box query APIs slowed down by join operations #1177.)
Severity (agent's assessment): low (each item)
Where: various, see list
Reproduction
Save as repro.py and run uv run repro.py (the PEP 723 header pins spatialdata to the commit the bug was found on; replace the URL fragment with @main to test the current main branch).
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "spatialdata @ git+https://github.com/scverse/spatialdata.git@ccf1ea048d054b6624214bf618008a9f9ae223e0",
# ]
# ///
"""Collection of small issues, each demonstrated below (see the issue text for the full list)."""
import warnings
import numpy as np
import pandas as pd
import geopandas as gpd
from anndata import AnnData
from shapely.geometry import Point
from spatialdata import SpatialData, get_element_instances
from spatialdata.models import Image2DModel, PointsModel, ShapesModel, TableModel
warnings.simplefilter("ignore")
found = []
print("1. empty GeoDataFrame -> IndexError instead of a clear ValueError")
try:
ShapesModel.parse(gpd.GeoDataFrame({"geometry": gpd.GeoSeries([], dtype="geometry")}))
except Exception as e: # noqa: BLE001
print(" ", type(e).__name__, str(e)[:60]); found.append(type(e).__name__ == "IndexError")
print("2. TableModel.parse(region=tuple / pd.Index) -> misleading errors")
obs = pd.DataFrame({"region": pd.Categorical(["a", "a", "b", "b"]), "instance_id": [0, 1, 0, 1]})
for reg in [("a", "b"), pd.Index(["a", "b"])]:
try:
TableModel.parse(AnnData(X=np.zeros((4, 1)), obs=obs.copy()), region=reg, region_key="region", instance_key="instance_id")
print(" ", type(reg).__name__, "OK")
except Exception as e: # noqa: BLE001
print(" ", type(reg).__name__, "->", type(e).__name__, str(e)[:70]); found.append(True)
print("3. RasterSchema.parse(chunks=<float>) -> ValueError from a wrong comprehension")
try:
Image2DModel.parse(np.zeros((1, 8, 8)), scale_factors=[2], chunks=4.0)
except Exception as e: # noqa: BLE001
print(" ", type(e).__name__, str(e)[:70]); found.append(True)
print("4. RasterSchema.parse mutates a numpy-backed input DataArray in place")
import xarray as xr
arr = xr.DataArray(np.zeros((1, 4, 4)), dims=("c", "y", "x"))
Image2DModel.parse(arr)
print(" input .data type after parse:", type(arr.data).__name__); found.append(type(arr.data).__name__ != "ndarray")
print("5. PointsModel.validate rejects pandas nullable / pyarrow numeric coordinate dtypes")
for dt in ["Int64", "float64[pyarrow]"]:
try:
PointsModel.parse(pd.DataFrame({"x": pd.array([1, 2], dtype=dt), "y": [1.0, 2.0]})); print(" ", dt, "OK")
except Exception as e: # noqa: BLE001
print(" ", dt, "->", type(e).__name__, str(e)[:60]); found.append(True)
print("6. get_element_instances(shapes, return_background=True) -> TypeError (overload lacks the keyword)")
shp = ShapesModel.parse(gpd.GeoDataFrame({"geometry": [Point(0, 0)], "radius": [1.0]}))
try:
get_element_instances(shp, return_background=True)
except Exception as e: # noqa: BLE001
print(" ", type(e).__name__, str(e)[:70]); found.append(True)
print("7. set_table_annotates_spatialelement(region=pd.Series) stores the Series in uns -> validate fails")
obs2 = pd.DataFrame({"region": pd.Categorical(["shp"]), "instance_id": [0]})
t = TableModel.parse(AnnData(X=np.zeros((1, 1)), obs=obs2), region="shp", region_key="region", instance_key="instance_id")
sdata = SpatialData(shapes={"shp": shp}, tables={"t": t})
sdata.set_table_annotates_spatialelement("t", region=pd.Series(["shp"]))
try:
TableModel.validate(sdata["t"]); print(" validate OK")
except Exception as e: # noqa: BLE001
print(" validate ->", type(e).__name__, str(e)[:60]); found.append(True)
print("8. filter_by_table_query(element_names=[...]) -> KeyError when no table row annotates those elements")
other = ShapesModel.parse(gpd.GeoDataFrame({"geometry": [Point(5, 5)], "radius": [1.0]}))
t2 = TableModel.parse(AnnData(X=np.zeros((1, 1)), obs=obs2.copy()), region="shp", region_key="region", instance_key="instance_id")
sdata2 = SpatialData(shapes={"shp": shp, "other": other}, tables={"t": t2})
try:
sdata2.filter_by_table_query("t", element_names=["other"]); print(" OK")
except Exception as e: # noqa: BLE001
print(" ->", type(e).__name__, str(e)[:60]); found.append(True)
print("VERDICT:", "BUG REPRODUCED" if all(found) and found else "NOT REPRODUCED")Observed output
1. empty GeoDataFrame -> IndexError instead of a clear ValueError
IndexError single positional indexer is out-of-bounds
2. TableModel.parse(region=tuple / pd.Index) -> misleading errors
tuple -> ValueError `adata.obs[region]` values do not match with `region` values.
Index -> TypeError unhashable type: 'Index'
3. RasterSchema.parse(chunks=<float>) -> ValueError from a wrong comprehension
ValueError not enough values to unpack (expected 2, got 1)
4. RasterSchema.parse mutates a numpy-backed input DataArray in place
input .data type after parse: Array
5. PointsModel.validate rejects pandas nullable / pyarrow numeric coordinate dtypes
Int64 -> ValueError Column `x` must be of type `int` or `float`.
float64[pyarrow] -> ValueError Column `x` must be of type `int` or `float`.
6. get_element_instances(shapes, return_background=True) -> TypeError (overload lacks the keyword)
TypeError _() got an unexpected keyword argument 'return_background'
7. set_table_annotates_spatialelement(region=pd.Series) stores the Series in uns -> validate fails
validate -> TypeError unhashable type: 'Series'
8. filter_by_table_query(element_names=[...]) -> KeyError when no table row annotates those elements
-> KeyError 't'
VERDICT: BUG REPRODUCED
Environment
uv run repro.py with the PEP 723 metadata in the script (fresh, isolated environment; spatialdata built from main @ ccf1ea0 (2026-08-28); Python 3.13, latest releases of the dependencies at run time: pandas 3.0, anndata 0.13, zarr 3.3, dask 2026.8, numpy 2.5, geopandas 1.1, shapely 2.1). macOS (arm64). Also reproduced in a second environment with pandas 2.3.3 / anndata 0.12.11 / numpy 2.4.4 / zarr 3.2.1.
Possibly related issues
Automatically generated; discovered by an AI agent (Claude) and not yet reviewed by a human.
Activity