Skip to content

Commit fdb039a

Browse files
authored
[PLT-1463] Remove deserialize completely (#1818)
1 parent f0aa377 commit fdb039a

19 files changed

+253
-560
lines changed

.github/workflows/lbox-develop.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: LBox Develop
22

33
on:
44
push:
5-
branches: [develop]
5+
branches: [develop, v6]
66
pull_request:
7-
branches: [develop]
7+
branches: [develop, v6]
88

99
concurrency:
1010
group: ${{ github.workflow }}-${{ github.ref }}

.github/workflows/python-package-develop.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Labelbox Python SDK Staging (Develop)
22

33
on:
44
push:
5-
branches: [develop]
5+
branches: [develop, v6]
66
pull_request:
7-
branches: [develop]
7+
branches: [develop, v6]
88

99
concurrency:
1010
group: ${{ github.workflow }}-${{ github.ref }}

libs/labelbox/src/labelbox/data/serialization/ndjson/base.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@
88

99
from ....annotated_types import Cuid
1010

11-
subclass_registry = {}
12-
13-
14-
class _SubclassRegistryBase(BaseModel):
15-
model_config = ConfigDict(extra="allow")
16-
17-
def __init_subclass__(cls, **kwargs):
18-
super().__init_subclass__(**kwargs)
19-
if cls.__name__ != "NDAnnotation":
20-
with threading.Lock():
21-
subclass_registry[cls.__name__] = cls
22-
2311

2412
class DataRow(_CamelCaseMixin):
2513
id: Optional[str] = None

libs/labelbox/src/labelbox/data/serialization/ndjson/classification.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
model_serializer,
3131
)
3232
from pydantic.alias_generators import to_camel
33-
from .base import _SubclassRegistryBase
3433

3534

3635
class NDAnswer(ConfidenceMixin, CustomMetricsMixin):
@@ -224,7 +223,7 @@ def from_common(
224223
# ====== End of subclasses
225224

226225

227-
class NDText(NDAnnotation, NDTextSubclass, _SubclassRegistryBase):
226+
class NDText(NDAnnotation, NDTextSubclass):
228227
@classmethod
229228
def from_common(
230229
cls,
@@ -249,9 +248,7 @@ def from_common(
249248
)
250249

251250

252-
class NDChecklist(
253-
NDAnnotation, NDChecklistSubclass, VideoSupported, _SubclassRegistryBase
254-
):
251+
class NDChecklist(NDAnnotation, NDChecklistSubclass, VideoSupported):
255252
@model_serializer(mode="wrap")
256253
def serialize_model(self, handler):
257254
res = handler(self)
@@ -298,9 +295,7 @@ def from_common(
298295
)
299296

300297

301-
class NDRadio(
302-
NDAnnotation, NDRadioSubclass, VideoSupported, _SubclassRegistryBase
303-
):
298+
class NDRadio(NDAnnotation, NDRadioSubclass, VideoSupported):
304299
@classmethod
305300
def from_common(
306301
cls,
@@ -343,7 +338,7 @@ def serialize_model(self, handler):
343338
return res
344339

345340

346-
class NDPromptText(NDAnnotation, NDPromptTextSubclass, _SubclassRegistryBase):
341+
class NDPromptText(NDAnnotation, NDPromptTextSubclass):
347342
@classmethod
348343
def from_common(
349344
cls,

libs/labelbox/src/labelbox/data/serialization/ndjson/converter.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,6 @@
2626

2727

2828
class NDJsonConverter:
29-
@staticmethod
30-
def deserialize(json_data: Iterable[Dict[str, Any]]) -> LabelGenerator:
31-
"""
32-
Converts ndjson data (prediction import format) into the common labelbox format.
33-
34-
Args:
35-
json_data: An iterable representing the ndjson data
36-
Returns:
37-
LabelGenerator containing the ndjson data.
38-
"""
39-
data = NDLabel(**{"annotations": copy.copy(json_data)})
40-
res = data.to_common()
41-
return res
42-
4329
@staticmethod
4430
def serialize(
4531
labels: LabelCollection,

libs/labelbox/src/labelbox/data/serialization/ndjson/label.py

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
from .relationship import NDRelationship
4747
from .base import DataRow
4848
from pydantic import BaseModel, ValidationError
49-
from .base import subclass_registry, _SubclassRegistryBase
5049
from pydantic_core import PydanticUndefined
5150
from contextlib import suppress
5251

@@ -67,68 +66,7 @@
6766

6867

6968
class NDLabel(BaseModel):
70-
annotations: List[_SubclassRegistryBase]
71-
72-
def __init__(self, **kwargs):
73-
# NOTE: Deserialization of subclasses in pydantic is difficult, see here https://blog.devgenius.io/deserialize-child-classes-with-pydantic-that-gonna-work-784230e1cf83
74-
# Below implements the subclass registry as mentioned in the article. The python dicts we pass in can be missing certain fields
75-
# we essentially have to infer the type against all sub classes that have the _SubclasssRegistryBase inheritance.
76-
# It works by checking if the keys of our annotations we are missing in matches any required subclass.
77-
# More keys are prioritized over less keys (closer match). This is used when importing json to our base models not a lot of customer workflows
78-
# depend on this method but this works for all our existing tests with the bonus of added validation. (no subclass found it throws an error)
79-
80-
for index, annotation in enumerate(kwargs["annotations"]):
81-
if isinstance(annotation, dict):
82-
item_annotation_keys = annotation.keys()
83-
key_subclass_combos = defaultdict(list)
84-
for subclass in subclass_registry.values():
85-
# Get all required keys from subclass
86-
annotation_keys = []
87-
for k, field in subclass.model_fields.items():
88-
if field.default == PydanticUndefined and k != "uuid":
89-
if (
90-
hasattr(field, "alias")
91-
and field.alias in item_annotation_keys
92-
):
93-
annotation_keys.append(field.alias)
94-
elif (
95-
hasattr(field, "validation_alias")
96-
and field.validation_alias
97-
in item_annotation_keys
98-
):
99-
annotation_keys.append(field.validation_alias)
100-
else:
101-
annotation_keys.append(k)
102-
103-
key_subclass_combos[subclass].extend(annotation_keys)
104-
105-
# Sort by subclass that has the most keys i.e. the one with the most keys that matches is most likely our subclass
106-
key_subclass_combos = dict(
107-
sorted(
108-
key_subclass_combos.items(),
109-
key=lambda x: len(x[1]),
110-
reverse=True,
111-
)
112-
)
113-
114-
for subclass, key_subclass_combo in key_subclass_combos.items():
115-
# Choose the keys from our dict we supplied that matches the required keys of a subclass
116-
check_required_keys = all(
117-
key in list(item_annotation_keys)
118-
for key in key_subclass_combo
119-
)
120-
if check_required_keys:
121-
# Keep trying subclasses until we find one that has valid values (does not throw an validation error)
122-
with suppress(ValidationError):
123-
annotation = subclass(**annotation)
124-
break
125-
if isinstance(annotation, dict):
126-
raise ValueError(
127-
f"Could not find subclass for fields: {item_annotation_keys}"
128-
)
129-
130-
kwargs["annotations"][index] = annotation
131-
super().__init__(**kwargs)
69+
annotations: AnnotationType
13270

13371
class _Relationship(BaseModel):
13472
"""This object holds information about the relationship"""

libs/labelbox/src/labelbox/data/serialization/ndjson/metric.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
ConfusionMatrixMetricConfidenceValue,
1616
)
1717
from pydantic import ConfigDict, model_serializer
18-
from .base import _SubclassRegistryBase
1918

2019

2120
class BaseNDMetric(NDJsonBase):
@@ -33,7 +32,7 @@ def serialize_model(self, handler):
3332
return res
3433

3534

36-
class NDConfusionMatrixMetric(BaseNDMetric, _SubclassRegistryBase):
35+
class NDConfusionMatrixMetric(BaseNDMetric):
3736
metric_value: Union[
3837
ConfusionMatrixMetricValue, ConfusionMatrixMetricConfidenceValue
3938
]
@@ -65,7 +64,7 @@ def from_common(
6564
)
6665

6766

68-
class NDScalarMetric(BaseNDMetric, _SubclassRegistryBase):
67+
class NDScalarMetric(BaseNDMetric):
6968
metric_value: Union[ScalarMetricValue, ScalarMetricConfidenceValue]
7069
metric_name: Optional[str] = None
7170
aggregation: Optional[ScalarMetricAggregation] = (

libs/labelbox/src/labelbox/data/serialization/ndjson/mmc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from labelbox.utils import _CamelCaseMixin
44

5-
from .base import _SubclassRegistryBase, DataRow, NDAnnotation
5+
from .base import DataRow, NDAnnotation
66
from ...annotation_types.mmc import (
77
MessageSingleSelectionTask,
88
MessageMultiSelectionTask,
@@ -20,7 +20,7 @@ class MessageTaskData(_CamelCaseMixin):
2020
]
2121

2222

23-
class NDMessageTask(NDAnnotation, _SubclassRegistryBase):
23+
class NDMessageTask(NDAnnotation):
2424
message_evaluation_task: MessageTaskData
2525

2626
def to_common(self) -> MessageEvaluationTaskAnnotation:

0 commit comments

Comments
 (0)