From e5de933c01efb39d5194d0ae33f2726ae9770595 Mon Sep 17 00:00:00 2001 From: Val Brodsky Date: Wed, 7 Aug 2024 15:34:09 -0700 Subject: [PATCH 1/2] Add an ability to pass in a config for a relationship --- libs/labelbox/src/labelbox/exceptions.py | 7 ++++++- libs/labelbox/src/labelbox/orm/db_object.py | 8 +++++++- libs/labelbox/src/labelbox/orm/model.py | 9 ++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/libs/labelbox/src/labelbox/exceptions.py b/libs/labelbox/src/labelbox/exceptions.py index 644fac0ab..048ca0757 100644 --- a/libs/labelbox/src/labelbox/exceptions.py +++ b/libs/labelbox/src/labelbox/exceptions.py @@ -44,7 +44,7 @@ def __init__(self, db_object_type=None, params=None, message=None): super().__init__(message) else: super().__init__("Resource '%s' not found for params: %r" % - (db_object_type.type_name(), params)) + (db_object_type.type_name(), params)) self.db_object_type = db_object_type self.params = params @@ -144,6 +144,11 @@ class OperationNotAllowedException(Exception): pass +class OperationNotSupportedException(Exception): + """Raised when sdk does not support requested operation""" + pass + + class ConfidenceNotSupportedException(Exception): """Raised when confidence is specified for unsupported annotation type""" diff --git a/libs/labelbox/src/labelbox/orm/db_object.py b/libs/labelbox/src/labelbox/orm/db_object.py index 326043d32..afa8fce49 100644 --- a/libs/labelbox/src/labelbox/orm/db_object.py +++ b/libs/labelbox/src/labelbox/orm/db_object.py @@ -1,10 +1,11 @@ +from dataclasses import dataclass from datetime import datetime, timezone from functools import wraps import logging import json from labelbox import utils -from labelbox.exceptions import InvalidQueryError, InvalidAttributeError +from labelbox.exceptions import InvalidQueryError, InvalidAttributeError, OperationNotSupportedException from labelbox.orm import query from labelbox.orm.model import Field, Relationship, Entity from labelbox.pagination import PaginatedCollection @@ -123,6 +124,7 @@ def __init__(self, source, relationship, value=None): self.supports_sorting = True self.filter_on_id = True self.value = value + self.config = relationship.config def __call__(self, *args, **kwargs): """ Forwards the call to either `_to_many` or `_to_one` methods, @@ -190,7 +192,11 @@ def connect(self, other): self.source.client.execute(query_string, params) def disconnect(self, other): + if not self.config.disconnect_supported: + raise OperationNotSupportedException( + "Disconnect is not supported for this relationship") """ Disconnects source object of this manager from the `other` object. """ + query_string, params = query.update_relationship( self.source, other, self.relationship, "disconnect") self.source.client.execute(query_string, params) diff --git a/libs/labelbox/src/labelbox/orm/model.py b/libs/labelbox/src/labelbox/orm/model.py index f3afa174e..5720b67cc 100644 --- a/libs/labelbox/src/labelbox/orm/model.py +++ b/libs/labelbox/src/labelbox/orm/model.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from enum import Enum, auto from typing import Dict, List, Union, Any, Type, TYPE_CHECKING @@ -219,6 +220,10 @@ class Relationship: """ + @dataclass + class Config: + disconnect_supported: bool = True + class Type(Enum): ToOne = auto() ToMany = auto() @@ -238,12 +243,14 @@ def __init__(self, name=None, graphql_name=None, cache=False, - deprecation_warning=None): + deprecation_warning=None, + config=Config()): self.relationship_type = relationship_type self.destination_type_name = destination_type_name self.filter_deleted = filter_deleted self.cache = cache self.deprecation_warning = deprecation_warning + self.config = config if name is None: name = utils.snake_case(destination_type_name) + ( From b9ab590979f6e08a3c55b4eed5298e2ac07ddf53 Mon Sep 17 00:00:00 2001 From: Val Brodsky Date: Wed, 7 Aug 2024 15:35:05 -0700 Subject: [PATCH 2/2] Raise exception to not allow project to disconnect labeling front end --- libs/labelbox/src/labelbox/orm/db_object.py | 2 +- libs/labelbox/src/labelbox/schema/project.py | 4 +++- libs/labelbox/tests/integration/test_labeling_frontend.py | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/labelbox/src/labelbox/orm/db_object.py b/libs/labelbox/src/labelbox/orm/db_object.py index afa8fce49..c4f87eac5 100644 --- a/libs/labelbox/src/labelbox/orm/db_object.py +++ b/libs/labelbox/src/labelbox/orm/db_object.py @@ -192,10 +192,10 @@ def connect(self, other): self.source.client.execute(query_string, params) def disconnect(self, other): + """ Disconnects source object of this manager from the `other` object. """ if not self.config.disconnect_supported: raise OperationNotSupportedException( "Disconnect is not supported for this relationship") - """ Disconnects source object of this manager from the `other` object. """ query_string, params = query.update_relationship( self.source, other, self.relationship, "disconnect") diff --git a/libs/labelbox/src/labelbox/schema/project.py b/libs/labelbox/src/labelbox/schema/project.py index 761d0e391..24e01442a 100644 --- a/libs/labelbox/src/labelbox/schema/project.py +++ b/libs/labelbox/src/labelbox/schema/project.py @@ -132,7 +132,9 @@ class Project(DbObject, Updateable, Deletable): # Relationships created_by = Relationship.ToOne("User", False, "created_by") organization = Relationship.ToOne("Organization", False) - labeling_frontend = Relationship.ToOne("LabelingFrontend") + labeling_frontend = Relationship.ToOne( + "LabelingFrontend", + config=Relationship.Config(disconnect_supported=False)) labeling_frontend_options = Relationship.ToMany( "LabelingFrontendOptions", False, "labeling_frontend_options") labeling_parameter_overrides = Relationship.ToMany( diff --git a/libs/labelbox/tests/integration/test_labeling_frontend.py b/libs/labelbox/tests/integration/test_labeling_frontend.py index 59ab0b924..d13871372 100644 --- a/libs/labelbox/tests/integration/test_labeling_frontend.py +++ b/libs/labelbox/tests/integration/test_labeling_frontend.py @@ -1,6 +1,8 @@ -from labelbox import LabelingFrontend import pytest +from labelbox import LabelingFrontend +from labelbox.exceptions import OperationNotSupportedException + def test_get_labeling_frontends(client): filtered_frontends = list( @@ -18,5 +20,5 @@ def test_labeling_frontend_connecting_to_project(project): project.labeling_frontend.connect(default_labeling_frontend) assert project.labeling_frontend() == default_labeling_frontend - project.labeling_frontend.disconnect(default_labeling_frontend) - assert project.labeling_frontend() == None + with pytest.raises(OperationNotSupportedException): + project.labeling_frontend.disconnect(default_labeling_frontend)