33
44import logging
55from pathlib import Path
6- from typing import Any , Dict , List , Optional , Sequence , Tuple , Type , Union , get_args
6+ from typing import (
7+ Any ,
8+ Dict ,
9+ List ,
10+ Optional ,
11+ Sequence ,
12+ Tuple ,
13+ Type ,
14+ Union ,
15+ cast ,
16+ get_args ,
17+ )
718
819import bioio_base as biob
920import dask .array as da
@@ -40,9 +51,22 @@ class BioImage(biob.image_container.ImageContainer):
4051 ----------
4152 image: biob.types.ImageLike
4253 A string, Path, fsspec supported URI, or arraylike to read.
43- reader: Optional[Type[Reader]]
44- The Reader class to specifically use for reading the provided image.
45- Default: None (find matching reader)
54+ reader: Optional[Union[Type[biob.reader.Reader],
55+ Sequence[Type[biob.reader.Reader]]]]
56+ Controls how BioImage selects the underlying Reader:
57+
58+ * If a **single Reader subclass** is provided (e.g. ``reader=TiffReader``),
59+ that reader is used directly and plugin ordering is bypassed.
60+
61+ * If a **sequence of Reader subclasses** is provided
62+ (e.g. ``reader=[TiffReader, OmeTiffReader]``), the list is treated as a
63+ *priority list* when selecting among installed plugins that support the
64+ file’s extension. Readers earlier in the list are tried first.
65+
66+ * A warning is raised if the priority list does not contain a
67+ reader that can read the provided image
68+
69+ * If ``None`` (default), BioImage uses its default plugin ordering.
4670 reconstruct_mosaic: bool
4771 Boolean for setting that data for this object to the reconstructed / stitched
4872 mosaic image.
@@ -60,15 +84,6 @@ class BioImage(biob.image_container.ImageContainer):
6084 checking for installed plugins on each `BioImage` instance init.
6185 If True, will use the cache of installed plugins discovered last `BioImage`
6286 init.
63- plugin_priority: Optional[Sequence[Type[biob.reader.Reader]]]
64- An optional ordered list of Reader classes that defines the priority used
65- when multiple plugins declare support for the same file type.
66-
67- When provided, the ordering in this list overrides BioIO’s default plugin
68- ordering for *that specific BioImage instance*. Only readers that actually
69- exist in the installed plugin registry may be included. Any readers not
70- present in the list will retain their original relative ordering after all
71- explicitly-prioritized readers.
7287 fs_kwargs: Dict[str, Any]
7388 Any specific keyword arguments to pass down to the fsspec created filesystem.
7489 Default: {}
@@ -122,12 +137,10 @@ class BioImage(biob.image_container.ImageContainer):
122137 >>> from bioio_ome_tiff import Reader as OmeTiffReader
123138 >>> img = BioImage(
124139 ... "multi_plugin_file.ome.tiff",
125- ... plugin_priority =[TiffReader, OmeTiffReader],
140+ ... reader =[TiffReader, OmeTiffReader],
126141 ... )
127142 ... # TiffReader will be tried before OmeTiffReader for this instance.
128143
129- Data for a mosaic file is returned pre-stitched (if the base reader supports it).
130-
131144 >>> img = BioImage("big_mosaic.czi")
132145 ... img.dims # <Dimensions [T: 40, C: 3, Z: 1, Y: 30000, X: 45000]>
133146
@@ -320,27 +333,42 @@ def ends_with_ext(string: str) -> bool:
320333 @staticmethod
321334 def _get_reader (
322335 image : biob .types .ImageLike ,
323- reader : biob .reader .Reader ,
336+ reader : Optional [
337+ Union [Type [biob .reader .Reader ], Sequence [Type [biob .reader .Reader ]]]
338+ ],
324339 use_plugin_cache : bool ,
325340 fs_kwargs : Dict [str , Any ],
326- plugin_priority : Optional [Sequence [Type [biob .reader .Reader ]]] = None ,
327341 ** kwargs : Any ,
328342 ) -> Tuple [biob .reader .Reader , Optional [PluginEntry ]]:
329343 """
330- Initializes and returns the reader (and plugin if relevant) for the provided
331- image based on provided args and/or the available bioio supported plugins
344+ Initialize and return the underlying reader (and its PluginEntry, if any)
345+ for the provided image.
346+
347+ Behavior
348+ --------
349+ * If ``reader`` is a single Reader subclass, that reader is used directly
350+ and the plugin system is bypassed.
351+
352+ * If ``reader`` is a sequence of Reader subclasses, it is treated as a
353+ priority list passed to the plugin system. Readers earlier in the list
354+ are tried first when multiple plugins support the same extension.
355+
356+ * If ``reader`` is ``None``, the default plugin ordering is used.
332357 """
333- if reader is not None :
334- # Check specific reader image types in a situation where a specified reader
335- # only supports some of the ImageLike types.
336- if not check_type (image , reader ):
358+
359+ # Force specific reader
360+ if isinstance (reader , type ):
361+ forced_reader = cast (Type [biob .reader .Reader ], reader )
362+ if not check_type (image , forced_reader ):
337363 raise biob .exceptions .UnsupportedFileFormatError (
338- reader .__name__ , str (type (image ))
364+ forced_reader .__name__ , str (type (image ))
339365 )
366+ return forced_reader (image , fs_kwargs = fs_kwargs , ** kwargs ), None
340367
341- return reader (image , fs_kwargs = fs_kwargs , ** kwargs ), None
342-
343- # Determine reader class based on available plugins
368+ # Determine plugin with priority
369+ plugin_priority : Optional [Sequence [Type [biob .reader .Reader ]]] = None
370+ if isinstance (reader , Sequence ):
371+ plugin_priority = reader
344372 plugin = BioImage .determine_plugin (
345373 image ,
346374 fs_kwargs = fs_kwargs ,
@@ -349,16 +377,28 @@ def _get_reader(
349377 ** kwargs ,
350378 )
351379 ReaderClass = plugin .metadata .get_reader ()
380+
381+ # Warn if user prioritized specific readers but none were usable
382+ if plugin_priority and ReaderClass not in plugin_priority :
383+ log .warning (
384+ "A reader priority list was provided (%s), but none of those "
385+ "readers could open '%s'. Falling back to '%s'." ,
386+ ", " .join (cls .__name__ for cls in plugin_priority ),
387+ image ,
388+ ReaderClass .__name__ ,
389+ )
390+
352391 return ReaderClass (image , fs_kwargs = fs_kwargs , ** kwargs ), plugin
353392
354393 def __init__ (
355394 self ,
356395 image : biob .types .ImageLike ,
357- reader : Optional [Type [biob .reader .Reader ]] = None ,
396+ reader : Optional [
397+ Union [Type [biob .reader .Reader ], Sequence [Type [biob .reader .Reader ]]]
398+ ] = None ,
358399 reconstruct_mosaic : bool = True ,
359400 use_plugin_cache : bool = False ,
360401 fs_kwargs : Dict [str , Any ] = {},
361- plugin_priority : Optional [Sequence [Type [biob .reader .Reader ]]] = None ,
362402 ** kwargs : Any ,
363403 ):
364404 try :
@@ -367,7 +407,6 @@ def __init__(
367407 reader ,
368408 use_plugin_cache ,
369409 fs_kwargs ,
370- plugin_priority = plugin_priority ,
371410 ** kwargs ,
372411 )
373412 except biob .exceptions .UnsupportedFileFormatError :
@@ -382,7 +421,6 @@ def __init__(
382421 reader ,
383422 use_plugin_cache ,
384423 fs_kwargs | {"anon" : True },
385- plugin_priority = plugin_priority ,
386424 ** kwargs ,
387425 )
388426
0 commit comments