Skip to content
This repository was archived by the owner on Dec 1, 2025. It is now read-only.

Commit 2716438

Browse files
ianhiAetherUnbound
andauthored
bugfix/Fix UnboundLocalError in TiffGlobReader (#449)
* Fix UnboundLocalError in TiffGlobReader * ensure indexer and fileseries indexes align * Make TIffGlobReader glob_in types more permissive * Update aicsimageio/tests/readers/test_glob_reader.py Co-authored-by: Madison Swain-Bowden <[email protected]> * get typing correct * apply black + fix test typing Co-authored-by: Madison Swain-Bowden <[email protected]>
1 parent f789666 commit 2716438

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

aicsimageio/readers/tiff_glob_reader.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
from collections import OrderedDict
77
from pathlib import Path
8-
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
8+
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union
99

1010
import dask.array as da
1111
import numpy as np
@@ -37,8 +37,8 @@ class TiffGlobReader(Reader):
3737
3838
Parameters
3939
----------
40-
glob_in: Union[PathLike, List[PathLike]]
41-
Glob string that identifies all files to be loaded or a list
40+
glob_in: Union[PathLike, Iterable[PathLike]]
41+
Glob string that identifies all files to be loaded or an iterable
4242
of paths to the files as returned by glob.
4343
indexer: Union[Callable, pandas.DataFrame]
4444
If callable, should consume each filename and return a pd.Series with series
@@ -125,7 +125,7 @@ def _is_supported_image(
125125

126126
def __init__(
127127
self,
128-
glob_in: Union[types.PathLike, List[types.PathLike]],
128+
glob_in: Union[types.PathLike, Iterable[types.PathLike]],
129129
indexer: Union[pd.DataFrame, Callable] = None,
130130
scene_glob_character: str = "S",
131131
chunk_dims: Union[str, List[str]] = DEFAULT_CHUNK_DIMS,
@@ -143,10 +143,15 @@ def __init__(
143143
# Assemble glob list if given a string
144144
if isinstance(glob_in, str):
145145
file_series = pd.Series(glob.glob(glob_in))
146-
elif isinstance(glob_in, list):
147-
file_series = pd.Series(glob_in)
148146
elif isinstance(glob_in, Path) and "*" in str(glob_in):
149147
file_series = pd.Series(glob.glob(str(glob_in)))
148+
elif isinstance(glob_in, pd.Series):
149+
# Ensure all of our indices line up
150+
file_series = glob_in.reset_index(drop=True, inplace=False)
151+
elif isinstance(glob_in, Iterable):
152+
file_series = pd.Series(glob_in)
153+
else:
154+
raise TypeError(f"Invalid type glob_in - got type {type(glob_in)}")
150155

151156
if len(file_series) == 0:
152157
raise ValueError("No files found matching glob pattern")
@@ -174,7 +179,10 @@ def indexer(x: str) -> pd.Series:
174179
self._all_files = file_series.apply(indexer)
175180
self._all_files["filename"] = file_series
176181
elif isinstance(indexer, pd.DataFrame):
177-
self._all_files = indexer
182+
# make a copy of the indexing dataframe and reset it index
183+
# to ensure that we don't generate nans when aligning with
184+
# file_series.
185+
self._all_files = indexer.reset_index(drop=True, inplace=False)
178186
self._all_files["filename"] = file_series
179187

180188
# If a dim doesn't exist on the file set the column value for that dim to zero.

aicsimageio/tests/readers/test_glob_reader.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
import os
33
from itertools import product
44
from pathlib import Path
5+
from typing import Any
56

67
import numpy as np
8+
import pandas as pd
9+
import pytest
710
import tifffile as tiff
811
import xarray as xr
912

@@ -68,6 +71,49 @@ def test_glob_reader_2d(tmp_path: Path) -> None:
6871
check_values(gr, reference)
6972

7073

74+
def test_index_alignment(tmp_path: Path) -> None:
75+
# Testing case where user has passed in a list of files
76+
# and a dataframe with non-continuous index
77+
78+
# use as_mm to have an easily available Indexer function
79+
_ = make_fake_data_2d(tmp_path, as_mm=True)
80+
filenames = np.array(list((tmp_path / "2d_images").glob("*.tif")))
81+
# print(filenames)
82+
indexer = pd.Series(filenames).apply(TiffGlobReader.MicroManagerIndexer)
83+
84+
# Keep only some of the Z
85+
# more realistic case is eliminating everything after a given time point
86+
# but this garuntees that our eliminated images will be embedded all through the
87+
# order rather than just at the end
88+
keep = indexer.Z < 5
89+
90+
indexer = indexer.loc[keep]
91+
92+
reader = TiffGlobReader(filenames[keep], indexer)
93+
94+
# check that there are no nans
95+
# nans are a symptom of index misalignment
96+
assert not reader._all_files.isnull().any().any()
97+
98+
99+
@pytest.mark.parametrize(
100+
"type_",
101+
[
102+
list,
103+
pd.Series,
104+
np.array,
105+
# should throw a TypeError instead of an unboundlocal error
106+
pytest.param(bytes, marks=pytest.mark.raises(exception=TypeError)),
107+
],
108+
)
109+
def test_glob_types(type_: Any, tmp_path: Path) -> None:
110+
reference = make_fake_data_2d(tmp_path)
111+
filenames = list((tmp_path / "2d_images").glob("*.tif"))
112+
113+
gr = TiffGlobReader(type_(filenames))
114+
check_values(gr, reference)
115+
116+
71117
def test_mm_indexer(tmp_path: Path) -> None:
72118
_ = make_fake_data_2d(tmp_path, True)
73119
gr = aicsimageio.readers.TiffGlobReader(

0 commit comments

Comments
 (0)