Skip to content

Commit a58da5b

Browse files
committed
Fix lint and integration errors
1 parent 3a7fb21 commit a58da5b

File tree

6 files changed

+62
-27
lines changed

6 files changed

+62
-27
lines changed

libs/labelbox/src/labelbox/client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,16 +507,16 @@ def delete_project_memberships(
507507
self, project_id: str, user_ids: list[str]
508508
) -> dict:
509509
"""Deletes project memberships for one or more users.
510-
510+
511511
Args:
512512
project_id (str): ID of the project
513513
user_ids (list[str]): List of user IDs to remove from the project
514-
514+
515515
Returns:
516516
dict: Result containing:
517517
- success (bool): True if operation succeeded
518518
- errorMessage (str or None): Error message if operation failed
519-
519+
520520
Example:
521521
>>> result = client.delete_project_memberships(
522522
>>> project_id="project123",
@@ -539,12 +539,12 @@ def delete_project_memberships(
539539
errorMessage
540540
}
541541
}"""
542-
542+
543543
params = {
544544
"projectId": project_id,
545545
"userIds": user_ids,
546546
}
547-
547+
548548
result = self.execute(mutation, params)
549549
return result["deleteProjectMemberships"]
550550

libs/labelbox/src/labelbox/schema/api_key.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ def _get_available_api_key_roles(client: "Client") -> List[str]:
258258
if role["name"] in ["None", "Tenant Admin"]:
259259
continue
260260
if all(perm in current_permissions for perm in role["permissions"]):
261-
available_roles.append(format_role(role["name"]))
261+
# Preserve server-provided role names (case-sensitive) so callers can
262+
# pass them through without normalization.
263+
available_roles.append(role["name"])
262264
client._cached_available_api_key_roles = available_roles
263265
return available_roles
264266

@@ -332,9 +334,25 @@ def create_api_key(
332334
raise ValueError("role must be a Role object or a valid role name")
333335

334336
allowed_roles = ApiKey._get_available_api_key_roles(client)
335-
# Format the input role name consistently with available roles
336-
formatted_role_name = format_role(role_name)
337-
if formatted_role_name not in allowed_roles:
337+
# Determine the exact server role name to pass through.
338+
#
339+
# - If caller provides a string, require exact match (case-sensitive).
340+
# - If caller provides a Role object (which may be normalized by the SDK),
341+
# map it back to the server role name.
342+
server_role_name: Optional[str] = None
343+
if hasattr(role, "name"):
344+
# Role objects in the SDK are often normalized (e.g. "TENANT_ADMIN").
345+
# Map normalized name back to the server-provided role display name.
346+
normalized_to_server = {format_role(r): r for r in allowed_roles}
347+
server_role_name = (
348+
role_name
349+
if role_name in allowed_roles
350+
else normalized_to_server.get(format_role(role_name))
351+
)
352+
else:
353+
server_role_name = role_name if role_name in allowed_roles else None
354+
355+
if server_role_name is None:
338356
raise ValueError(
339357
f"Invalid role specified. Allowed roles are: {allowed_roles}"
340358
)
@@ -371,7 +389,7 @@ def create_api_key(
371389
params = {
372390
"name": name,
373391
"userEmail": user_email,
374-
"role": role_name,
392+
"role": server_role_name,
375393
"validitySeconds": validity_seconds,
376394
}
377395

libs/labelbox/src/labelbox/schema/project.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,9 @@ def get_resource_tags(self) -> List[ResourceTag]:
317317

318318
return [ResourceTag(self.client, tag) for tag in results]
319319

320-
def labels(self, datasets=None, order_by=None, created_by=None) -> PaginatedCollection:
320+
def labels(
321+
self, datasets=None, order_by=None, created_by=None
322+
) -> PaginatedCollection:
321323
"""Custom relationship expansion method to support limited filtering.
322324
323325
Args:
@@ -334,7 +336,7 @@ def labels(self, datasets=None, order_by=None, created_by=None) -> PaginatedColl
334336
Example:
335337
>>> # Get all labels
336338
>>> all_labels = project.labels()
337-
>>>
339+
>>>
338340
>>> # Get labels by specific user
339341
>>> user_labels = project.labels(created_by=user_id)
340342
>>> # or
@@ -351,16 +353,22 @@ def labels(self, datasets=None, order_by=None, created_by=None) -> PaginatedColl
351353

352354
# Build where clause
353355
where_clauses = []
354-
356+
355357
if datasets is not None:
356-
dataset_ids = ", ".join('"%s"' % dataset.uid for dataset in datasets)
357-
where_clauses.append(f"dataRow: {{dataset: {{id_in: [{dataset_ids}]}}}}")
358-
358+
dataset_ids = ", ".join(
359+
'"%s"' % dataset.uid for dataset in datasets
360+
)
361+
where_clauses.append(
362+
f"dataRow: {{dataset: {{id_in: [{dataset_ids}]}}}}"
363+
)
364+
359365
if created_by is not None:
360366
# Handle both User object and user_id string
361-
user_id = created_by.uid if hasattr(created_by, 'uid') else created_by
367+
user_id = (
368+
created_by.uid if hasattr(created_by, "uid") else created_by
369+
)
362370
where_clauses.append(f'createdBy: {{id: "{user_id}"}}')
363-
371+
364372
if where_clauses:
365373
where = " where:{" + ", ".join(where_clauses) + "}"
366374
else:
@@ -396,7 +404,7 @@ def labels(self, datasets=None, order_by=None, created_by=None) -> PaginatedColl
396404

397405
def delete_labels_by_user(self, user_id: str) -> int:
398406
"""Soft deletes all labels created by a specific user in this project.
399-
407+
400408
This performs a soft delete (sets deleted=true in the database).
401409
The labels will no longer appear in queries but remain in the database.
402410
Labels are deleted in chunks of 500 to avoid overwhelming the API.
@@ -413,18 +421,18 @@ def delete_labels_by_user(self, user_id: str) -> int:
413421
>>> print(f"Deleted {deleted_count} labels")
414422
"""
415423
labels_to_delete = list(self.labels(created_by=user_id))
416-
424+
417425
if not labels_to_delete:
418426
return 0
419-
427+
420428
chunk_size = 500
421429
total_deleted = 0
422-
430+
423431
for i in range(0, len(labels_to_delete), chunk_size):
424-
chunk = labels_to_delete[i:i + chunk_size]
432+
chunk = labels_to_delete[i : i + chunk_size]
425433
Entity.Label.bulk_delete(chunk)
426434
total_deleted += len(chunk)
427-
435+
428436
return total_deleted
429437

430438
def export(

libs/labelbox/src/labelbox/schema/user_group.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from collections import defaultdict
1010
from dataclasses import dataclass
1111
from enum import Enum
12+
import uuid
1213
from typing import Any, Dict, Iterator, List, Optional, Set
1314

1415
from lbox.exceptions import (
@@ -415,6 +416,14 @@ def delete(self) -> bool:
415416
if not self.id:
416417
raise ValueError("Group id is required")
417418

419+
# The API expects a UUID-formatted identifier and may respond with an
420+
# internal server error if the value cannot be parsed. Validate client-side
421+
# so callers get a consistent exception.
422+
try:
423+
uuid.UUID(str(self.id))
424+
except Exception as e:
425+
raise MalformedQueryException("Invalid user group id") from e
426+
418427
query = """
419428
mutation DeleteUserGroupPyApi($id: ID!) {
420429
deleteUserGroup(where: {id: $id}) {

libs/labelbox/tests/integration/test_project_set_model_setup_complete.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def test_live_chat_evaluation_project_delete_cofig(
3636

3737
with pytest.raises(
3838
expected_exception=LabelboxError,
39-
match="Cannot create model config for project because model setup is complete",
39+
match="Cannot (create model config for project because model setup is complete|perform this action because model setup is complete)",
4040
):
4141
project_model_config.delete()
4242

libs/labelbox/tests/unit/schema/test_user_group.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def test_delete(self):
341341
"deleteUserGroup": {"success": True}
342342
}
343343
group = self.group
344-
group.id = "group_id"
344+
group.id = "11111111-2222-3333-4444-555555555555"
345345
result = group.delete()
346346
assert result is True
347347

@@ -350,7 +350,7 @@ def test_delete_resource_not_found_error(self):
350350
message="Not found"
351351
)
352352
group = self.group
353-
group.id = "group_id"
353+
group.id = "11111111-2222-3333-4444-555555555555"
354354
with pytest.raises(ResourceNotFoundError):
355355
group.delete()
356356

0 commit comments

Comments
 (0)