Skip to content

Commit 9b79444

Browse files
Extra fixes adata layers None after anndata X unification (#1123)
* Fix adata layers None after anndata X unification * Handle layers[None] (X) correctly in sanitize_table and add test coverage Skip None when building new_keys so it is never passed to sanitize_name, and preserve it verbatim in new_dict so the layers setter round-trips X correctly on anndata >= 0.13 where X lives at layers[None]. Add test_sanitize_table_layers_preserves_x to verify X is intact after sanitization; the None-in-layers assertion is guarded by a pre-check so the test is valid on both anndata 0.12.x and >= 0.13. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 824f960 commit 9b79444

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

src/spatialdata/_core/_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ def get_unique_name(name: str, attr: str, is_dataframe_column: bool = False) ->
158158
# Handle other attributes
159159
for attr in ("obsm", "obsp", "varm", "varp", "uns", "layers"):
160160
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()}
164164
setattr(sanitized, attr, new_dict)
165165

166166
return None if inplace else sanitized

tests/utils/test_sanitize.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,22 @@ def test_sanitize_table_uns_and_layers():
164164
assert list(sanitized.layers.keys()) == ["bad_layer"]
165165

166166

167+
def test_sanitize_table_layers_preserves_x():
168+
# anndata >= 0.13 stores X as layers[None]; sanitize_table must not corrupt it
169+
X = np.array([[0, 1], [1, 0]])
170+
ad = AnnData(X=X, obs=pd.DataFrame({"x": [1, 2]}, index=["0", "1"]), var=pd.DataFrame(index=["v1", "v2"]))
171+
ad.layers["bad#layer"] = np.array([[1, 0], [0, 1]])
172+
none_in_layers_before = None in ad.layers
173+
sanitized = sanitize_table(ad, inplace=False)
174+
assert sanitized.X is not None
175+
np.testing.assert_array_equal(sanitized.X, X)
176+
string_keys = [k for k in sanitized.layers if k is not None]
177+
assert string_keys == ["bad_layer"]
178+
# If anndata stores X as layers[None], the None key must survive sanitization
179+
if none_in_layers_before:
180+
assert None in sanitized.layers
181+
182+
167183
def test_sanitize_table_empty_returns_empty():
168184
ad = AnnData()
169185
sanitized = sanitize_table(ad, inplace=False)

0 commit comments

Comments
 (0)