spatialdata icon indicating copy to clipboard operation
spatialdata copied to clipboard

Uncaught error during pretty print

Open falexwolf opened this issue 8 months ago • 2 comments

After writing the object to disk, the pretty print functionality triggered by just displaying the sdata blobs object in a notebook runs into this uncaught error.

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/spatialdata/_core/spatialdata.py:1073, in SpatialData.elements_paths_on_disk(self)
   1071 if self.path is None:
   1072     raise ValueError("The SpatialData object is not backed by a Zarr store.")
-> 1073 store = parse_url(self.path, mode="r").store
   1074 root = zarr.group(store=store)
   1075 elements_in_zarr = []

AttributeError: 'NoneType' object has no attribute 'store'

The return value of parse_url() is None even though self.path is None didn't trigger.

Full traceback

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/IPython/core/formatters.py:770, in PlainTextFormatter.__call__(self, obj)
    763 stream = StringIO()
    764 printer = pretty.RepresentationPrinter(stream, self.verbose,
    765     self.max_width, self.newline,
    766     max_seq_length=self.max_seq_length,
    767     singleton_pprinters=self.singleton_printers,
    768     type_pprinters=self.type_printers,
    769     deferred_pprinters=self.deferred_printers)
--> 770 printer.pretty(obj)
    771 printer.flush()
    772 return stream.getvalue()

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/IPython/lib/pretty.py:411, in RepresentationPrinter.pretty(self, obj)
    400                         return meth(obj, self, cycle)
    401                 if (
    402                     cls is not object
    403                     # check if cls defines __repr__
   (...)    409                     and callable(_safe_getattr(cls, "__repr__", None))
    410                 ):
--> 411                     return _repr_pprint(obj, self, cycle)
    413     return _default_pprint(obj, self, cycle)
    414 finally:

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/IPython/lib/pretty.py:786, in _repr_pprint(obj, p, cycle)
    784 """A pprint that just redirects to the normal repr function."""
    785 # Find newlines and replace them with p.break_()
--> 786 output = repr(obj)
    787 lines = output.splitlines()
    788 with p.group():

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/spatialdata/_core/spatialdata.py:1971, in SpatialData.__repr__(self)
   1970 def __repr__(self) -> str:
-> 1971     return self._gen_repr()

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/spatialdata/_core/spatialdata.py:2121, in SpatialData._gen_repr(self)
   2118             descr += f"\n    ▸ {element_name}: {backing_files}"
   2120 if self.path is not None:
-> 2121     elements_only_in_sdata, elements_only_in_zarr = self._symmetric_difference_with_zarr_store()
   2122     if len(elements_only_in_sdata) > 0:
   2123         descr += "\nwith the following elements not in the Zarr store:"

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/spatialdata/_core/spatialdata.py:1105, in SpatialData._symmetric_difference_with_zarr_store(self)
   1088 """
   1089 Determine if elements in the SpatialData object are different from elements saved in the Zarr store.
   1090 
   (...)   1102 The paths are relative to the root of the SpatialData object and are in the format "element_type/element_name".
   1103 """
   1104 elements_in_sdata = self.elements_paths_in_memory()
-> 1105 elements_in_zarr = self.elements_paths_on_disk()
   1107 elements_only_in_sdata = list(set(elements_in_sdata).difference(set(elements_in_zarr)))
   1108 elements_only_in_zarr = list(set(elements_in_zarr).difference(set(elements_in_sdata)))

File /opt/hostedtoolcache/Python/3.12.10/x64/lib/python3.12/site-packages/spatialdata/_core/spatialdata.py:1073, in SpatialData.elements_paths_on_disk(self)
   1071 if self.path is None:
   1072     raise ValueError("The SpatialData object is not backed by a Zarr store.")
-> 1073 store = parse_url(self.path, mode="r").store
   1074 root = zarr.group(store=store)
   1075 elements_in_zarr = []

AttributeError: 'NoneType' object has no attribute 'store'

Immediately before this, I get this logging message from spatialdata:

INFO     The Zarr backing store has been changed from None the new file path:                                      
         /home/runner/.cache/lamindb/l5UsjZhknzRYx1FV0000.zarr 

falexwolf avatar Apr 18 '25 10:04 falexwolf

When a SpatialData object is relocated on disk, its __repr__ method fails because it attempts to access files at the original location stored in spatialdata.path. For example in https://github.com/scverse/spatialdata/blob/eb5a20257b2fdb60eb3bad815b1ff43964f941c9/src/spatialdata/_core/spatialdata.py#L2119 it attempts to access data on disk but the spatialdata.path has changed.

A better approach would be to redesign the __repr__ to rely only on in-memory state, avoiding disk access entirely. This would make the representation more robust when objects are moved or when files are inaccessible.

Zethson avatar May 09 '25 10:05 Zethson

Hi, I cannot reproduce the bug. I have tried with this code

from spatialdata.datasets import blobs
from tempfile import TemporaryDirectory
from pathlib import Path

with TemporaryDirectory() as tmp_dir:
    sdata = blobs()
    sdata.write(Path(tmp_dir) / "blobs.zarr")
    print(sdata)
    sdata.write(Path(tmp_dir) / "blobs2.zarr")
    print(sdata)

Can you please adjust it to obtain the bug you found?

LucaMarconato avatar Aug 19 '25 09:08 LucaMarconato