Skip to content

Commit e56f736

Browse files
authored
Merge pull request #850 from superannotateai/FRIDAY-3798
fix in ItemContext
2 parents 24f4b9f + b472b6a commit e56f736

2 files changed

Lines changed: 69 additions & 1 deletion

File tree

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def __init__(
155155
self._annotation_adapter: BaseMultimodalAnnotationAdapter | None = None
156156
self._overwrite = overwrite
157157
self._annotation: dict | None = None
158+
self._set_component_called = False
158159

159160
def _set_small_annotation_adapter(self, annotation: dict | None = None):
160161
self._annotation_adapter = MultimodalSmallAnnotationAdapter(
@@ -224,7 +225,9 @@ def save(self):
224225
self._set_large_annotation_adapter(self.annotation)
225226
else:
226227
self._set_small_annotation_adapter(self.annotation)
227-
self._annotation_adapter.save()
228+
if self._set_component_called:
229+
self._annotation_adapter.save()
230+
self._set_component_called = False
228231

229232
def get_metadata(self):
230233
"""
@@ -284,6 +287,7 @@ def set_component_value(self, component_id: str, value: Any):
284287
285288
"""
286289
self.annotation_adapter.set_component_value(component_id, value)
290+
self._set_component_called = True
287291
return self
288292

289293

tests/integration/items/test_item_context.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import json
22
import os
33
from pathlib import Path
4+
from unittest import TestCase
5+
from unittest.mock import MagicMock
6+
from unittest.mock import patch
47

58
from src.superannotate import FileChangedError
9+
from src.superannotate import ItemContext
610
from src.superannotate import SAClient
711
from tests.integration.base import BaseTestCase
812

@@ -135,3 +139,63 @@ def tearDown(self) -> None:
135139
sa.delete_project(self.PROJECT_NAME)
136140
except Exception:
137141
...
142+
143+
144+
class TestItemContextSetComponentCalledFlag(TestCase):
145+
def _make_context(self):
146+
ic = ItemContext(
147+
controller=MagicMock(),
148+
project=MagicMock(),
149+
folder=MagicMock(),
150+
item=MagicMock(),
151+
overwrite=True,
152+
)
153+
ic._annotation_adapter = MagicMock()
154+
ic._annotation_adapter.annotation = {"metadata": {}, "data": {}}
155+
return ic
156+
157+
def test_dirty_flag_initial_state(self):
158+
ic = self._make_context()
159+
self.assertFalse(ic._set_component_called)
160+
161+
def test_set_component_value_marks_dirty(self):
162+
ic = self._make_context()
163+
ic.set_component_value("component_id", "value")
164+
self.assertTrue(ic._set_component_called)
165+
166+
def test_save_called_on_exit_after_set_component_value(self):
167+
ic = self._make_context()
168+
with patch.object(ItemContext, "save", autospec=True) as save_mock:
169+
with ic:
170+
ic.set_component_value("component_id", "value")
171+
save_mock.assert_called_once_with(ic)
172+
173+
def test_dirty_flag_reset_after_save(self):
174+
ic = self._make_context()
175+
with patch.object(ic, "_set_small_annotation_adapter"), patch.object(
176+
ic, "_set_large_annotation_adapter"
177+
):
178+
ic.set_component_value("component_id", "value")
179+
self.assertTrue(ic._set_component_called)
180+
ic.save()
181+
self.assertFalse(ic._set_component_called)
182+
183+
def test_no_double_save_on_exit_after_manual_save(self):
184+
ic = self._make_context()
185+
with patch.object(ic, "_set_small_annotation_adapter"), patch.object(
186+
ic, "_set_large_annotation_adapter"
187+
):
188+
with ic:
189+
ic.set_component_value("component_id", "value")
190+
ic.save()
191+
self.assertEqual(ic._annotation_adapter.save.call_count, 1)
192+
self.assertEqual(ic._annotation_adapter.save.call_count, 1)
193+
194+
def test_save_not_called_when_exception_raised(self):
195+
ic = self._make_context()
196+
with patch.object(ItemContext, "save", autospec=True) as save_mock:
197+
with self.assertRaises(RuntimeError):
198+
with ic:
199+
ic.set_component_value("component_id", "value")
200+
raise RuntimeError("boom")
201+
save_mock.assert_not_called()

0 commit comments

Comments
 (0)