diff --git a/deeplake/api/tests/test_api.py b/deeplake/api/tests/test_api.py index d24ec6ae3a..e2a84fc532 100644 --- a/deeplake/api/tests/test_api.py +++ b/deeplake/api/tests/test_api.py @@ -1988,6 +1988,31 @@ def test_partial_read_then_write(s3_ds_generator): ds.xyz[1] = 20 * np.ones((1000, 1000)) +def test_allow_new_labels(local_ds): + with local_ds as ds: + ds.create_tensor("dynamic_labels", htype="class_label") + ds.create_tensor( + "fixed_labels", + htype="class_label", + class_names=["a", "b", "c"], + allow_new_labels=False, + ) + + ds.dynamic_labels.append("a") + ds.dynamic_labels.append("a") + ds.fixed_labels.append("a") + ds.fixed_labels.append("b") + assert ds.dynamic_labels.info["class_names"] == ["a"] + assert ds.fixed_labels.info["class_names"] == ["a", "b", "c"] + + with pytest.raises(SampleAppendError): + ds.fixed_labels.append("new_one") + + ds.fixed_labels.info.update(allow_new_labels=False) + with pytest.raises(SampleAppendError): + ds.fixed_labels.append("new_one") + + def test_exist_ok(local_ds): with local_ds as ds: ds.create_tensor("abc") diff --git a/deeplake/core/chunk_engine.py b/deeplake/core/chunk_engine.py index 92e472fccb..6597f84774 100644 --- a/deeplake/core/chunk_engine.py +++ b/deeplake/core/chunk_engine.py @@ -789,6 +789,13 @@ def _convert_class_labels(self, samples): class_names = tensor_info.class_names labels, additions = convert_to_idx(samples, class_names) if additions: + if ( + "allow_new_labels" in tensor_info + and tensor_info["allow_new_labels"] is False + ): + raise ValueError( + f"New label(s) [{','.join([i[0] for i in additions])}] are not allowed for tensor {tensor_name}. Allowed labels are: [{','.join(tensor_info.class_names)}]" + ) for new in additions: class_names.append(new[0]) logger.info( diff --git a/deeplake/htype.py b/deeplake/htype.py index 2933f20932..aaa75a59c2 100644 --- a/deeplake/htype.py +++ b/deeplake/htype.py @@ -64,7 +64,11 @@ class htype: htype.CLASS_LABEL: { "dtype": "uint32", "class_names": [], - "_info": ["class_names"], # class_names should be stored in info, not meta + "allow_new_labels": True, + "_info": [ + "class_names", + "allow_new_labels", + ], # class_names should be stored in info, not meta "_disable_temp_transform": False, }, htype.BBOX: {"dtype": "float32", "coords": {}, "_info": ["coords"]},