Skip to content

Commit cdd49ee

Browse files
committed
bug(fcsfile): fix recursion when file has no spill string
1 parent 47dcff1 commit cdd49ee

File tree

3 files changed

+64
-12
lines changed

3 files changed

+64
-12
lines changed

cellengine/resources/fcs_file.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class FcsFile(DataClassMixin):
3535
sample_name: Optional[str] = field(default=ReadOnly(optional=True)) # type: ignore
3636
size: int = field(default=ReadOnly()) # type: ignore
3737
_spill_string: Optional[str] = field(
38-
metadata=config(field_name="spillString"), default=None
38+
metadata=config(field_name="spillString"),
39+
default=None,
3940
)
4041

4142
def __repr__(self):
@@ -197,6 +198,10 @@ def plot(
197198

198199
def get_file_internal_compensation(self) -> Compensation:
199200
"""Get the file-internal Compensation."""
201+
if not self.has_file_internal_comp:
202+
raise ValueError(
203+
f"FCS File '{self._id}' does not have an internal compensation."
204+
)
200205
return Compensation.from_spill_string(self.spill_string)
201206

202207
@property
@@ -223,12 +228,11 @@ def events(self, events):
223228

224229
@property
225230
def spill_string(self):
226-
if self._spill_string:
227-
return self._spill_string
228-
else:
229-
ss = ce.APIClient().get_fcs_file(self.experiment_id, self._id).spill_string
230-
self._spill_string = ss
231-
return ss
231+
if not self._spill_string and self.has_file_internal_comp:
232+
self._spill_string = ce.APIClient().get_fcs_file(
233+
self.experiment_id, self._id, as_dict=True
234+
)["spillString"]
235+
return self._spill_string or ""
232236

233237
def get_events(
234238
self, inplace: bool = False, destination=None, **kwargs

cellengine/utils/api_client/APIClient.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,29 @@ def get_fcs_files(self, experiment_id, as_dict=False) -> List[FcsFile]:
320320
return fcs_files
321321
return [FcsFile.from_dict(fcs_file) for fcs_file in fcs_files]
322322

323+
# fmt: off
324+
@overload
323325
def get_fcs_file(
324-
self, experiment_id, _id=None, name=None, as_dict=False
325-
) -> FcsFile:
326+
self,
327+
experiment_id: str,
328+
_id: str = None,
329+
name: str = None,
330+
as_dict: bool = True,
331+
) -> Dict[str, Any]: ...
332+
333+
@overload
334+
def get_fcs_file(
335+
self,
336+
experiment_id: str,
337+
_id: str = None,
338+
name: str = None,
339+
as_dict: bool = False,
340+
) -> FcsFile: ...
341+
# fmt: on
342+
343+
def get_fcs_file(
344+
self, experiment_id: str, _id: str = None, name: str = None, as_dict=False
345+
):
326346
_id = _id or self._get_id_by_name(name, "fcsfiles", experiment_id)
327347
fcs_file = self._get(
328348
f"{self.base_url}/experiments/{experiment_id}/fcsfiles/{_id}"

tests/unit/resources/test_fcsfile.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import os
1+
from io import BufferedReader, BytesIO
22
import json
3+
import os
4+
35
from fcsparser.api import FCSParser
46
from pandas.core.frame import DataFrame
7+
import pytest
58
import responses
6-
from io import BufferedReader, BytesIO
79

8-
from cellengine.resources.fcs_file import FcsFile
910
from cellengine.resources.compensation import Compensation
11+
from cellengine.resources.fcs_file import FcsFile
1012

1113

1214
EXP_ID = "5d38a6f79fae87499999a74b"
@@ -79,6 +81,32 @@ def test_gets_file_internal_compensation(ENDPOINT_BASE, client, fcs_files, spill
7981
assert type(comp) == Compensation
8082

8183

84+
@responses.activate
85+
def test_throws_correct_error_when_no_file_internal_compensation(
86+
ENDPOINT_BASE, client, fcs_files
87+
):
88+
# Given: An FcsFile with no spill string
89+
file_data = fcs_files[0]
90+
file_data["spillString"] = None
91+
file_data["hasFileInternalComp"] = False
92+
file = FcsFile.from_dict(file_data)
93+
expected_response = fcs_files[0].copy()
94+
responses.add(
95+
responses.GET,
96+
f"{ENDPOINT_BASE}/experiments/{EXP_ID}/fcsfiles/{file._id}",
97+
json=expected_response,
98+
)
99+
100+
# When:
101+
with pytest.raises(ValueError) as err:
102+
comp = file.get_file_internal_compensation()
103+
# Then:
104+
assert (
105+
err.value.args[0]
106+
== f"FCS File '{file._id}' does not have an internal compensation."
107+
)
108+
109+
82110
def test_parse_fcs_file():
83111
events_body = open("tests/data/Acea - Novocyte.fcs", "rb")
84112
parser = FCSParser.from_data(events_body.read())

0 commit comments

Comments
 (0)