@@ -823,6 +823,60 @@ def test_render_labels_disjoint_instance_ids_clear_error():
823823 plt .close (fig )
824824
825825
826+ def _labels_with_partial_table (instance_id , * , scale_factors = None ):
827+ # labels contain instances 1, 2; the table's instance_id set is caller-controlled so tests can
828+ # add phantom rows (instances absent from the raster) or omit a present instance.
829+ arr = np .zeros ((20 , 20 ), dtype = np .int32 )
830+ arr [3 :8 , 3 :8 ] = 1
831+ arr [12 :17 , 12 :17 ] = 2
832+ obs = pd .DataFrame (
833+ {
834+ "instance_id" : instance_id ,
835+ "region" : pd .Categorical (["lbl" ] * len (instance_id )),
836+ "cat" : pd .Categorical ([c for c , _ in zip ("ABCDEFG" , instance_id , strict = False )]),
837+ }
838+ )
839+ obs .index = obs .index .astype (str )
840+ table = TableModel .parse (
841+ AnnData (X = np .zeros ((len (instance_id ), 1 )), obs = obs ),
842+ region = ["lbl" ],
843+ region_key = "region" ,
844+ instance_key = "instance_id" ,
845+ )
846+ table .obs ["value" ] = np .arange (len (instance_id ), dtype = float )
847+ labels = Labels2DModel .parse (arr , dims = ["y" , "x" ], scale_factors = scale_factors )
848+ return SpatialData (labels = {"lbl" : labels }, tables = {"t" : table })
849+
850+
851+ @pytest .mark .parametrize ("color" , ["value" , "cat" ])
852+ @pytest .mark .parametrize (
853+ "instance_id" ,
854+ [
855+ [1 , 2 , 3 ], # phantom row: instance 3 is absent from the raster (#775)
856+ [1 ], # present instance 2 has no table row
857+ ],
858+ )
859+ def test_render_labels_partial_table_does_not_raise (color , instance_id ):
860+ # Regression test for #775: a table annotating instances that are not present in the labels
861+ # (or missing a present instance) must render as missing, not raise IndexError.
862+ sdata = _labels_with_partial_table (instance_id )
863+ fig , ax = plt .subplots ()
864+ try :
865+ sdata .pl .render_labels ("lbl" , color = color , table_name = "t" ).pl .show (ax = ax )
866+ finally :
867+ plt .close (fig )
868+
869+
870+ def test_render_labels_phantom_row_survives_rasterization ():
871+ # #775: the alignment must also hold once rasterization/multiscale drops labels from the raster.
872+ sdata = _labels_with_partial_table ([1 , 2 , 3 ], scale_factors = [2 ])
873+ fig , ax = plt .subplots ()
874+ try :
875+ sdata .pl .render_labels ("lbl" , color = "value" , table_name = "t" ).pl .show (ax = ax )
876+ finally :
877+ plt .close (fig )
878+
879+
826880@pytest .mark .parametrize ("scale_factors" , [None , [2 ]])
827881def test_render_labels_raises_on_3d (scale_factors ):
828882 # Regression test for #608: 3D labels must raise a clear ValueError, not crash
0 commit comments