Skip to content

Commit 496e7a2

Browse files
authored
Merge pull request #811 from Labelbox/AL-4502-improved-error-handling-for-single-annotation-uploads
Al 4502 improved error handling for single annotation uploads
2 parents 2b686ee + f41f3a5 commit 496e7a2

File tree

4 files changed

+92
-20
lines changed

4 files changed

+92
-20
lines changed

labelbox/schema/annotation_import.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ def _create_from_bytes(cls, client, variables, query_str, file_name,
141141
files = {file_name: file_data}
142142
return client.execute(data=data, files=files)
143143

144+
@classmethod
145+
def _get_ndjson_from_objects(cls, objects: List[Dict[str, Any]],
146+
object_name: str) -> BinaryIO:
147+
if not isinstance(objects, list):
148+
raise TypeError(
149+
f"{object_name} must be in a form of list. Found {type(objects)}"
150+
)
151+
152+
data_str = ndjson.dumps(objects)
153+
if not data_str:
154+
raise ValueError(f"{object_name} cannot be empty")
155+
156+
return data_str.encode('utf-8')
157+
144158
def refresh(self) -> None:
145159
"""Synchronizes values of all fields with the database.
146160
"""
@@ -197,8 +211,9 @@ def create_from_file(cls, client: "labelbox.Client", model_run_id: str,
197211
raise ValueError(f"File {path} is not accessible")
198212

199213
@classmethod
200-
def create_from_objects(cls, client: "labelbox.Client", model_run_id: str,
201-
name, predictions) -> "MEAPredictionImport":
214+
def create_from_objects(
215+
cls, client: "labelbox.Client", model_run_id: str, name,
216+
predictions: List[Dict[str, Any]]) -> "MEAPredictionImport":
202217
"""
203218
Create an MEA prediction import job from an in memory dictionary
204219
@@ -210,12 +225,10 @@ def create_from_objects(cls, client: "labelbox.Client", model_run_id: str,
210225
Returns:
211226
MEAPredictionImport
212227
"""
213-
data_str = ndjson.dumps(predictions)
214-
if not data_str:
215-
raise ValueError('annotations cannot be empty')
216-
data = data_str.encode('utf-8')
228+
data = cls._get_ndjson_from_objects(predictions, 'annotations')
229+
217230
return cls._create_mea_import_from_bytes(client, model_run_id, name,
218-
data, len(data))
231+
data, len(str(data)))
219232

220233
@classmethod
221234
def create_from_url(cls, client: "labelbox.Client", model_run_id: str,
@@ -448,19 +461,16 @@ def create_from_objects(
448461
Returns:
449462
MALPredictionImport
450463
"""
451-
data_str = ndjson.dumps(predictions)
452-
if not data_str:
453-
raise ValueError('annotations cannot be empty')
454-
data = data_str.encode('utf-8')
464+
data = cls._get_ndjson_from_objects(predictions, 'annotations')
455465

456466
has_confidence = LabelsConfidencePresenceChecker.check(predictions)
457467
if has_confidence:
458468
logger.warning("""
459-
Confidence scores are not supported in MAL Prediction Import.
460-
Corresponding confidence score values will be ingored.
469+
Confidence scores are not supported in MAL Prediction Import.
470+
Corresponding confidence score values will be ignored.
461471
""")
462472
return cls._create_mal_import_from_bytes(client, project_id, name, data,
463-
len(data))
473+
len(str(data)))
464474

465475
@classmethod
466476
def create_from_url(cls, client: "labelbox.Client", project_id: str,
@@ -607,19 +617,16 @@ def create_from_objects(cls, client: "labelbox.Client", project_id: str,
607617
Returns:
608618
LabelImport
609619
"""
610-
data_str = ndjson.dumps(labels)
611-
if not data_str:
612-
raise ValueError('labels cannot be empty')
613-
data = data_str.encode('utf-8')
620+
data = cls._get_ndjson_from_objects(labels, 'labels')
614621

615622
has_confidence = LabelsConfidencePresenceChecker.check(labels)
616623
if has_confidence:
617624
logger.warning("""
618-
Confidence scores are not supported in Label Import.
625+
Confidence scores are not supported in Label Import.
619626
Corresponding confidence score values will be ignored.
620627
""")
621628
return cls._create_label_import_from_bytes(client, project_id, name,
622-
data, len(data))
629+
data, len(str(data)))
623630

624631
@classmethod
625632
def create_from_url(cls, client: "labelbox.Client", project_id: str,

labelbox/schema/bulk_import_request.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,11 @@ def create_from_objects(cls,
310310
Returns:
311311
BulkImportRequest object
312312
"""
313+
if not isinstance(predictions, list):
314+
raise TypeError(
315+
f"annotations must be in a form of Iterable. Found {type(predictions)}"
316+
)
317+
313318
if validate:
314319
_validate_ndjson(predictions, client.get_project(project_id))
315320

tests/unit/test_mal_import.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import uuid
2+
import pytest
23
from unittest.mock import MagicMock, patch
34

45
from labelbox.schema.annotation_import import MALPredictionImport, logger
@@ -39,3 +40,36 @@ def test_should_warn_user_about_unsupported_confidence():
3940
warning_mock.assert_called_once()
4041
"Confidence scores are not supported in MAL Prediction Import" in warning_mock.call_args_list[
4142
0].args[0]
43+
44+
45+
def test_invalid_labels_format():
46+
"""this test should confirm that annotations are required to be in a form of list"""
47+
id = str(uuid.uuid4())
48+
49+
label = {
50+
"bbox": {
51+
"height": 428,
52+
"left": 2089,
53+
"top": 1251,
54+
"width": 158
55+
},
56+
"classifications": [{
57+
"answer": [{
58+
"schemaId": "ckrb1sfl8099e0y919v260awv",
59+
"confidence": 0.894
60+
}],
61+
"schemaId": "ckrb1sfkn099c0y910wbo0p1a"
62+
}],
63+
"dataRow": {
64+
"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"
65+
},
66+
"schemaId": "ckrb1sfjx099a0y914hl319ie",
67+
"uuid": "d009925d-91a3-4f67-abd9-753453f5a584"
68+
}
69+
70+
with patch.object(MALPredictionImport, '_create_mal_import_from_bytes'):
71+
with pytest.raises(TypeError):
72+
MALPredictionImport.create_from_objects(client=MagicMock(),
73+
project_id=id,
74+
name=id,
75+
predictions=label)

tests/unit/test_unit_label_import.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import uuid
2+
import pytest
23
from unittest.mock import MagicMock, patch
34

45
from labelbox.schema.annotation_import import LabelImport, logger
@@ -33,3 +34,28 @@ def test_should_warn_user_about_unsupported_confidence():
3334
warning_mock.assert_called_once()
3435
"Confidence scores are not supported in Label Import" in warning_mock.call_args_list[
3536
0].args[0]
37+
38+
39+
def test_invalid_labels_format():
40+
"""this test should confirm that labels are required to be in a form of list"""
41+
id = str(uuid.uuid4())
42+
43+
label = {
44+
"uuid": "b862c586-8614-483c-b5e6-82810f70cac0",
45+
"schemaId": "ckrazcueb16og0z6609jj7y3y",
46+
"dataRow": {
47+
"id": "ckrazctum0z8a0ybc0b0o0g0v"
48+
},
49+
"bbox": {
50+
"top": 1352,
51+
"left": 2275,
52+
"height": 350,
53+
"width": 139
54+
}
55+
}
56+
with patch.object(LabelImport, '_create_label_import_from_bytes'):
57+
with pytest.raises(TypeError):
58+
LabelImport.create_from_objects(client=MagicMock(),
59+
project_id=id,
60+
name=id,
61+
labels=label)

0 commit comments

Comments
 (0)