scverse / scverse/napari-spatialdata
KeyError: 'SpatialData'
Open
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 90
- Forks
- 25
- Avg merge
- 22m
- Merged PRs (30d)
- 1
Description
Hi,
I installed napari and napari-spatialdata plugin from the conda-forge repo, via pixi.
Whenever I try to open the spatial data plugin (view or scatter) from the UI, I get this error stack:
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/app_model/backends/qt/_qaction.py:59, in QCommandAction._on_triggered(self=QMenuItemAction(MenuItem(when=None, group='3_plu...napari spatialdata)'))), alt=None), app='napari'), checked=True)
55 def _on_triggered(self, checked: bool) -> None:
56 # execute_command returns a Future, for the sake of eventually being
57 # asynchronous without breaking the API. For now, we call result()
58 # to raise any exceptions.
---> 59 self._app.commands.execute_command(self._command_id).result()
self._command_id = 'napari-spatialdata:Scatter'
self = QMenuItemAction(MenuItem(when=None, group='3_plugin_contributions', order=None, command=CommandRule(id='napari-spatialdata:Scatter', title='Scatter', category=None, tooltip=None, status_tip=None, icon=None, icon_visible_in_menu=True, enablement=None, short_title=None, toggled=ToggleRule(condition=None, get_current=functools.partial(<function _get_current_dock_status at 0x16ccca050>, full_name='Scatter (napari spatialdata)'))), alt=None), app='napari')
self._app = Application('napari')
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/app_model/registries/_commands_reg.py:247, in CommandsRegistry.execute_command(self=<CommandsRegistry at 0x16cd00d00 (165 commands)>, id='napari-spatialdata:Scatter', execute_asynchronously=False, *args=(), **kwargs={})
243 except Exception as e:
244 if self._raise_synchronous_exceptions:
245 # note, the caller of this function can also achieve this by
246 # calling `future.result()` on the returned future object.
--> 247 raise e
248 future.set_exception(e)
250 return future
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/app_model/registries/_commands_reg.py:242, in CommandsRegistry.execute_command(self=<CommandsRegistry at 0x16cd00d00 (165 commands)>, id='napari-spatialdata:Scatter', execute_asynchronously=False, *args=(), **kwargs={})
240 future: Future = Future()
241 try:
--> 242 future.set_result(cmd(*args, **kwargs))
future = <Future at 0x3011a29e0 state=pending>
cmd = <function Store.inject.<locals>._inner.<locals>._exec at 0x3011913f0>
args = ()
kwargs = {}
243 except Exception as e:
244 if self._raise_synchronous_exceptions:
245 # note, the caller of this function can also achieve this by
246 # calling `future.result()` on the returned future object.
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/in_n_out/_store.py:934, in Store.inject_processors.<locals>._deco.<locals>._exec(*args=(), **kwargs={})
932 @wraps(func)
933 def _exec(*args: P.args, **kwargs: P.kwargs) -> R:
--> 934 result = func(*args, **kwargs)
func = <function Store.inject.<locals>._inner.<locals>._exec at 0x301191090>
args = ()
kwargs = {}
935 if result is not None:
936 self.process(
937 result,
938 type_hint=type_hint,
(...)
941 _funcname=getattr(func, "__qualname__", str(func)),
942 )
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/in_n_out/_store.py:804, in Store.inject.<locals>._inner.<locals>._exec(*args=(), **kwargs={})
797 logger.debug(
798 " Calling %s with %r (injected %r)",
799 _fname,
800 bound.arguments,
801 _injected_names,
802 )
803 try:
--> 804 result = func(**bound.arguments)
bound = <BoundArguments (plugin='napari-spatialdata', widget_name='Scatter', full_name='Scatter (napari spatialdata)')>
func = functools.partial(<function _toggle_or_get_widget at 0x16ccc9fc0>, plugin='napari-spatialdata', widget_name='Scatter', full_name='Scatter (napari spatialdata)')
bound.arguments = {'plugin': 'napari-spatialdata', 'widget_name': 'Scatter', 'full_name': 'Scatter (napari spatialdata)'}
805 except TypeError as e:
806 if "missing" not in e.args[0]:
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/napari/_qt/_qplugins/_qnpe2.py:352, in _toggle_or_get_widget(plugin='napari-spatialdata', widget_name='Scatter', full_name='Scatter (napari spatialdata)')
350 if widget_param:
351 kwargs[widget_param] = viewer
--> 352 return widget_callable(**kwargs), full_name
kwargs = {'napari_viewer': Viewer(camera=Camera(center=(0.0, 0.0, 0.0), zoom=1.0, angles=(0.0, 0.0, 90.0), perspective=0.0, mouse_pan=True, mouse_zoom=True, orientation=(<DepthAxisOrientation.TOWARDS: 'towards'>, <VerticalAxisOrientation.DOWN: 'down'>, <HorizontalAxisOrientation.RIGHT: 'right'>)), cursor=Cursor(position=(1.0, 1.0), viewbox=None, scaled=True, style=<CursorStyle.STANDARD: 'standard'>, size=1.0), dims=Dims(ndim=2, ndisplay=2, order=(0, 1), axis_labels=('0', '1'), rollable=(True, True), range=(RangeTuple(start=0.0, stop=2.0, step=1.0), RangeTuple(start=0.0, stop=2.0, step=1.0)), margin_left=(0.0, 0.0), margin_right=(0.0, 0.0), point=(np.float64(0.0), np.float64(0.0)), last_used=0), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False, spacing=0.0), layers=[], help='', status='Ready', tooltip=Tooltip(visible=False, text=''), theme='dark', title='napari', mouse_over_canvas=True, mouse_move_callbacks=[], mouse_drag_callbacks=[<function drag_to_zoom at 0x16b167760>], mouse_double_click_callbacks=[<function double_click_to_zoom at 0x16b1676d0>], mouse_wheel_callbacks=[<function dims_scroll at 0x16b166d40>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, _keymap={})}
widget_callable = <class 'napari_spatialdata._view.QtAdataScatterWidget'>
full_name = 'Scatter (napari spatialdata)'
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/site-packages/napari_spatialdata/_view.py:54, in QtAdataScatterWidget.__init__(self=<napari_spatialdata._view.QtAdataScatterWidget object>, napari_viewer=Viewer(camera=Camera(center=(0.0, 0.0, 0.0), zoo...use_drag_gen={}, _mouse_wheel_gen={}, _keymap={}), model=None)
48 def __init__(self, napari_viewer: Viewer, model: DataModel | None = None):
49 super().__init__()
51 self._model = (
52 model
53 if model is not None
---> 54 else napari_viewer.window._dock_widgets["SpatialData"].widget().viewer_model._model
self = <napari_spatialdata._view.QtAdataScatterWidget object at 0x301191000>
model = None
model is not None = False
napari_viewer = Viewer(camera=Camera(center=(0.0, 0.0, 0.0), zoom=1.0, angles=(0.0, 0.0, 90.0), perspective=0.0, mouse_pan=True, mouse_zoom=True, orientation=(<DepthAxisOrientation.TOWARDS: 'towards'>, <VerticalAxisOrientation.DOWN: 'down'>, <HorizontalAxisOrientation.RIGHT: 'right'>)), cursor=Cursor(position=(1.0, 1.0), viewbox=None, scaled=True, style=<CursorStyle.STANDARD: 'standard'>, size=1.0), dims=Dims(ndim=2, ndisplay=2, order=(0, 1), axis_labels=('0', '1'), rollable=(True, True), range=(RangeTuple(start=0.0, stop=2.0, step=1.0), RangeTuple(start=0.0, stop=2.0, step=1.0)), margin_left=(0.0, 0.0), margin_right=(0.0, 0.0), point=(np.float64(0.0), np.float64(0.0)), last_used=0), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False, spacing=0.0), layers=[], help='', status='Ready', tooltip=Tooltip(visible=False, text=''), theme='dark', title='napari', mouse_over_canvas=True, mouse_move_callbacks=[], mouse_drag_callbacks=[<function drag_to_zoom at 0x16b167760>], mouse_double_click_callbacks=[<function double_click_to_zoom at 0x16b1676d0>], mouse_wheel_callbacks=[<function dims_scroll at 0x16b166d40>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, _keymap={})
55 )
57 self.setLayout(QGridLayout())
59 self._viewer = napari_viewer
File ~/Documents/dev/napari/.pixi/envs/default/lib/python3.10/weakref.py:137, in WeakValueDictionary.__getitem__(self=<WeakValueDictionary>, key='SpatialData')
135 if self._pending_removals:
136 self._commit_removals()
--> 137 o = self.data[key]()
self = <WeakValueDictionary at 0x17d60ce20>
self.data = {}
key = 'SpatialData'
138 if o is None:
139 raise KeyError(key)
KeyError: 'SpatialData'
Sample of my environment (macOS):
napari 0.6.6 pyh4da3b27_0 8.3 KiB conda https://conda.anaconda.org/conda-forge/
napari-base 0.6.6 pyhcf101f3_0 2.7 MiB conda https://conda.anaconda.org/conda-forge/
napari-console 0.1.4 pyhc04f84c_0 15.3 KiB conda https://conda.anaconda.org/conda-forge/
napari-matplotlib 3.0.2 pyhc04f84c_0 326.2 KiB conda https://conda.anaconda.org/conda-forge/
napari-plugin-engine 0.2.0 pyha07c04f_3 32.4 KiB conda https://conda.anaconda.org/conda-forge/
napari-plugin-manager 0.1.9 pyhd8ed1ab_0 44.2 KiB conda https://conda.anaconda.org/conda-forge/
napari-spatialdata 0.5.4.post0 pyh73487a3_0 83 KiB conda https://conda.anaconda.org/conda-forge/
napari-svg 0.2.1 pyha07c04f_0 19.5 KiB conda https://conda.anaconda.org/conda-forge/
python 3.10.19 hcd7f573_2_cpython 11.1 MiB conda https://conda.anaconda.org/conda-forge/
python-build 1.3.0 pyhff2d567_0 25.5 KiB conda https://conda.anaconda.org/conda-forge/
python-dateutil 2.9.0.post0 pyhe01879c_2 227.8 KiB conda https://conda.anaconda.org/conda-forge/
python-tzdata 2025.3 pyhd8ed1ab_0 140.2 KiB conda https://conda.anaconda.org/conda-forge/
python_abi 3.10 8_cp310 6.8 KiB conda https://conda.anaconda.org/conda-forge/
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with napari_spatialdata/_view.py, especially QtAdataScatterWidget.init, and reproduce the failure by opening Scatter or View from the napari UI. Trace how the SpatialData dock widget is registered and verify that both actions open without raising KeyError: 'SpatialData'.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- data-visualization, frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100