Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bioio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python

"""Top-level package for bioio."""

Expand Down
16 changes: 11 additions & 5 deletions bioio/bio_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,17 @@ def save(
ids in each Image's name attribute but IDs will be generated. The order of the
scenes will be the same (or whatever order was specified / provided).
"""
from .writers import OmeTiffWriter
import bioio.writers as _writers # type: ignore[attr-defined]

OmeTiffWriter = getattr(_writers, "OmeTiffWriter", None)

if OmeTiffWriter is None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why log this if bioio-ome-tiff isn't installed? Wouldn't we also need to do this for ome-zarr if we go this direction?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We implement a specific save function built into BioImage that defaults to the OmeTiffWriter. It never had the option of the OmeZarrWriter. This just maintains the old functionality but warns the user if OmeTiffWriter isnt installed since it comes from bioio-ome-tiff now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see that now - weird that we did that. Makes sense to include to until we do a major version change

Copy link
Contributor Author

@BrianWhitneyAI BrianWhitneyAI May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is another argument for a more involved ABC in bioio_base for writers. Then we could implement a way to choose what writer to use for save @toloudis what do you think about this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was written when OME-TIFF was clearly the only important open file format we wanted, and before we were dealing with multi-TB multiscene time series, nevermind chunked zarr 😄 . Right now I question whether a single save function in the BioImage class even is useful.

The code change as implemented here seems to make sense for maintaining existing functionality...
but I think long term if writers are being removed from bioio, then either (a) save should be removed from BioImage, or (b) save could get a abstract Writer passed in and the caller would have to do the importing and constructing the writer object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this moving of writers seems to be a breaking change anyway, maybe this is a good opportunity to remove save until we design something better (abstracted api or not)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have plans to add it back later I think that the current option is a better one for now. Itd be strange to have one release without the save function. hopefully this will be minimally breaking since all the imports remain the same and the usage is the same

log.warning(
"TIFF writing support (OmeTiffWriter) is not available. "
"To enable it, install the plugin:\n"
" pip install bioio-ome-tiff"
)
return

# Get all parameters as dict of lists, or static because of unchanging values
datas: List[biob.types.ArrayLike] = []
Expand All @@ -1165,15 +1175,11 @@ def save(
image_names: List[Optional[str]] = []
physical_pixel_sizes: List[biob.types.PhysicalPixelSizes] = []

# Get selected scenes / handle None scenes
if select_scenes is None:
select_scenes = self.scenes

# Iter through scenes to get data
for scene in select_scenes:
self.set_scene(scene)

# Append this scene details
datas.append(self.dask_data)
dim_orders.append(self.dims.order)
channel_names.append(self.channel_names)
Expand Down
2 changes: 1 addition & 1 deletion bioio/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python

"""Unit test package for bioio."""
22 changes: 22 additions & 0 deletions bioio/tests/dummy-plugin/dummy_plugin/writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Any

import bioio_base as biob
from bioio_base.writer import Writer


class DummyWriter(Writer):
"""
A dummy writer for testing bioio.writers entry-point discovery.
"""

@staticmethod
def save(
data: biob.types.ArrayLike,
uri: biob.types.PathLike,
dim_order: str = biob.dimensions.DEFAULT_DIMENSION_ORDER,
**kwargs: Any,
) -> None:
"""
Stub implementation that deliberately isn’t implemented.
"""
raise NotImplementedError("DummyWriter.save is a stub")
3 changes: 3 additions & 0 deletions bioio/tests/dummy-plugin/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ test = [
[project.entry-points."bioio.readers"]
dummy-plugin = "dummy_plugin"

[project.entry-points."bioio.writers"]
DummyWriter = "dummy_plugin.writer:DummyWriter"

# build settings
# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
[tool.setuptools]
Expand Down
28 changes: 28 additions & 0 deletions bioio/tests/test_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from importlib.metadata import entry_points

import pytest


def test_dummy_writer_discovery_and_api(dummy_plugin: str) -> None:
# Entry-point registration
eps = entry_points(group="bioio.writers")
names = {ep.name for ep in eps}
assert "DummyWriter" in names, f"found: {sorted(names)}"

# Public API (__all__)
import bioio.writers as pkg

assert "DummyWriter" in getattr(pkg, "__all__", [])

# Import works (dynamic, so ignore mypy attr-defined error)
from bioio.writers import DummyWriter # type: ignore[attr-defined]

assert DummyWriter.__name__ == "DummyWriter"


def test_dummy_writer_save_stub(dummy_plugin: str) -> None:
# The save() stub should raise NotImplementedError
from bioio.writers import DummyWriter # type: ignore[attr-defined]

with pytest.raises(NotImplementedError):
DummyWriter.save(data=[1], uri="unused", dim_order="XYZ")
2 changes: 0 additions & 2 deletions bioio/tests/writers/__init__.py

This file was deleted.

Loading