diff --git a/src/mudata/_core/mudata.py b/src/mudata/_core/mudata.py index a2cdf87..caedeeb 100644 --- a/src/mudata/_core/mudata.py +++ b/src/mudata/_core/mudata.py @@ -204,16 +204,20 @@ def __init__( if isinstance(self._var, abc.Mapping) or self._var is None: self._var = pd.DataFrame(self._var) + # Replace missing/None with empty dictionaries + for attr in ("obsm", "varm", "obsp", "varp", "obsmap", "varmap"): + if kwargs.get(attr, None) is None: + kwargs[attr] = {} # Get global obsm - self._obsm = MuAxisArrays(self, axis=0, store=kwargs.get("obsm", {})) + self._obsm = MuAxisArrays(self, axis=0, store=kwargs.get("obsm")) # Get global varm - self._varm = MuAxisArrays(self, axis=1, store=kwargs.get("varm", {})) + self._varm = MuAxisArrays(self, axis=1, store=kwargs.get("varm")) - self._obsp = PairwiseArrays(self, axis=0, store=kwargs.get("obsp", {})) - self._varp = PairwiseArrays(self, axis=1, store=kwargs.get("varp", {})) + self._obsp = PairwiseArrays(self, axis=0, store=kwargs.get("obsp")) + self._varp = PairwiseArrays(self, axis=1, store=kwargs.get("varp")) - self._obsmap = MuAxisArrays(self, axis=0, store=kwargs.get("obsmap", {})) - self._varmap = MuAxisArrays(self, axis=1, store=kwargs.get("varmap", {})) + self._obsmap = MuAxisArrays(self, axis=0, store=kwargs.get("obsmap")) + self._varmap = MuAxisArrays(self, axis=1, store=kwargs.get("varmap")) self._axis = kwargs.get("axis") or 0 diff --git a/tests/data/archives/README.md b/tests/data/archives/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/archives/v0.1.2/create_testfiles.py b/tests/data/archives/v0.1.2/create_testfiles.py new file mode 100644 index 0000000..c38b849 --- /dev/null +++ b/tests/data/archives/v0.1.2/create_testfiles.py @@ -0,0 +1,45 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "mudata==0.1.2", +# "numpy==1.*", # required as numpy 2 raises errors +# ] +# /// +import os + +import numpy as np +from anndata import AnnData + +from mudata import MuData + + +def create_mudata() -> MuData: + """Create a mudata testfile + + Returns: + MuData: MuData object with two AnnData objects + """ + return MuData( + { + "mod1": AnnData(np.arange(0, 100, 0.1).reshape(-1, 10)), + "mod2": AnnData(np.arange(101, 2101, 1).reshape(-1, 20)), + } + ) + + +def write_h5mu_testfile(mdata: MuData, filepath_h5mu: str | os.PathLike) -> None: + """Write a MuData object to a file + + Args: + mdata (MuData): MuData object to write + filepath_h5mu (str | os.PathLike): Path to write the MuData object + + Returns: + None + """ + mdata.write(filepath_h5mu) + + +if __name__ == "__main__": + mdata = create_mudata() + write_h5mu_testfile(mdata, "mudata.h5mu") diff --git a/tests/data/archives/v0.1.2/mudata.h5mu b/tests/data/archives/v0.1.2/mudata.h5mu new file mode 100644 index 0000000..adb9812 Binary files /dev/null and b/tests/data/archives/v0.1.2/mudata.h5mu differ diff --git a/tests/test_io_backwards_compat.py b/tests/test_io_backwards_compat.py new file mode 100644 index 0000000..8ae84ed --- /dev/null +++ b/tests/test_io_backwards_compat.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from pathlib import Path + +import numpy as np +import pytest +from anndata import AnnData +from anndata.tests.helpers import assert_equal + +from mudata import MuData, read_h5mu + +ARCHIVE_PTH = Path(__file__).parent / "data/archives" + + +@pytest.fixture(params=list(ARCHIVE_PTH.glob("v*")), ids=lambda x: x.name) +def archive_dir(request): + return request.param + + +@pytest.fixture +def mdata(): + return MuData( + { + "mod1": AnnData(np.arange(0, 100, 0.1).reshape(-1, 10)), + "mod2": AnnData(np.arange(101, 2101, 1).reshape(-1, 20)), + } + ) + + +class TestLegacyMuData: + def test_legacy_files(self, archive_dir, mdata): + from_h5mu = read_h5mu(archive_dir / "mudata.h5mu") + + for mod in mdata.mod: + assert_equal(from_h5mu.mod[mod].X, mdata.mod[mod].X, exact=False)