FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Add dataset tags to SDK for identification (DE-7033) (#456) · scaleapi/nucleus-python-client@5c4b847 · GitHub

Commit 5c4b847

Browse files
andauthored
Add dataset tags to SDK for identification (DE-7033) (#456)
Expose dataset tags through the Python SDK so customers can identify datasets labeled by Scale vs other vendors via the API. - Add `tags` field to DatasetInfo model (returned by dataset.info()) - Add get_tags(), add_tags(), remove_tags() methods to Dataset class - Use POST /tags/remove instead of DELETE to avoid proxy body-stripping - Use pydantic v1/v2 compat shim for null-coercion validator - Guard against passing a bare string instead of a list Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3321deb commit 5c4b847

5 files changed

Lines changed: 99 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to the [Nucleus Python Client](https://github.com/scaleapi/n
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.18.2](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.18.2) - 2026-05-08
9+
10+
### Added
11+
- Dataset tags are now exposed through the SDK so customers can identify datasets labeled by Scale vs other vendors. `Dataset.info()` now returns a `tags` field, and `Dataset` exposes `get_tags()`, `add_tags()`, and `remove_tags()` methods.
12+
813
## [0.18.1](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.18.1) - 2026-05-05
914

1015
### Changed

‎nucleus/data_transfer_object/dataset_info.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
from typing import Any, Dict, List, Optional
1+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
2+
3+
if TYPE_CHECKING:
4+
from pydantic.v1 import validator
5+
else:
6+
try:
7+
from pydantic.v1 import validator
8+
except ImportError:
9+
from pydantic import validator
210

311
from nucleus.pydantic_base import DictCompatibleModel
412

@@ -14,6 +22,7 @@ class DatasetInfo(DictCompatibleModel):
1422
slice_ids: List :class:`Slice` IDs associated with the :class:`Dataset`
1523
annotation_metadata_schema: Dict defining annotation-level metadata schema.
1624
item_metadata_schema: Dict defining item metadata schema.
25+
tags: List of tags associated with the :class:`Dataset`.
1726
"""
1827

1928
dataset_id: str
@@ -24,3 +33,8 @@ class DatasetInfo(DictCompatibleModel):
2433
# TODO: Expand the following into pydantic models to formalize schema
2534
annotation_metadata_schema: Optional[Dict[str, Any]] = None
2635
item_metadata_schema: Optional[Dict[str, Any]] = None
36+
tags: List[str] = []
37+
38+
@validator("tags", pre=True, always=True) # pylint: disable=used-before-assignment
39+
def coerce_null_tags(cls, v): # pylint: disable=no-self-argument
40+
return v if v is not None else []

‎nucleus/dataset.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,49 @@ def info(self) -> DatasetInfo:
433433
dataset_info = DatasetInfo.parse_obj(response)
434434
return dataset_info
435435

436+
def get_tags(self) -> List[str]:
437+
"""Fetches tags associated with the dataset.
438+
439+
Returns:
440+
List of tag strings associated with this dataset.
441+
"""
442+
response = self._client.make_request(
443+
{}, f"dataset/{self.id}/tags", requests.get
444+
)
445+
return response["tags"]
446+
447+
def add_tags(self, tags: List[str]) -> List[str]:
448+
"""Adds tags to the dataset.
449+
450+
Args:
451+
tags: List of tag strings to add.
452+
453+
Returns:
454+
Updated list of all tags on the dataset.
455+
"""
456+
if isinstance(tags, str):
457+
raise TypeError("tags must be a list of strings, not a single string")
458+
response = self._client.make_request(
459+
{"tags": tags}, f"dataset/{self.id}/tags", requests.post
460+
)
461+
return response["tags"]
462+
463+
def remove_tags(self, tags: List[str]) -> List[str]:
464+
"""Removes tags from the dataset.
465+
466+
Args:
467+
tags: List of tag strings to remove.
468+
469+
Returns:
470+
Updated list of remaining tags on the dataset.
471+
"""
472+
if isinstance(tags, str):
473+
raise TypeError("tags must be a list of strings, not a single string")
474+
response = self._client.make_request(
475+
{"tags": tags}, f"dataset/{self.id}/tags", requests.delete
476+
)
477+
return response["tags"]
478+
436479
@deprecated(
437480
"Model runs have been deprecated and will be removed. Use a Model instead"
438481
)

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ignore = ["E501", "E741", "E731", "F401"] # Easy ignore for getting it running
2525

2626
[tool.poetry]
2727
name = "scale-nucleus"
28-
version = "0.18.1"
28+
version = "0.18.2"
2929
description = "The official Python client library for Nucleus, the Data Platform for AI"
3030
license = "MIT"
3131
authors = ["Scale AI Nucleus Team <nucleusapi@scaleapi.com>"]

‎tests/test_dataset.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,41 @@ def test_dataset_slices(CLIENT, dataset):
257257
# TODO(gunnar): Test slice items -> Split up info!
258258

259259

260+
def test_dataset_tags(CLIENT, dataset):
261+
# Fresh dataset should have no tags
262+
assert dataset.get_tags() == []
263+
264+
# Add tags
265+
updated = dataset.add_tags(["Labeled by: Scale", "production"])
266+
assert "Labeled by: Scale" in updated
267+
assert "production" in updated
268+
269+
# Info should include tags
270+
info = dataset.info()
271+
assert "Labeled by: Scale" in info.tags
272+
assert "production" in info.tags
273+
274+
# Adding duplicate tags is idempotent
275+
updated2 = dataset.add_tags(["production", "v2"])
276+
assert "production" in updated2
277+
assert "v2" in updated2
278+
279+
# Remove tags
280+
remaining = dataset.remove_tags(["production"])
281+
assert "production" not in remaining
282+
assert "Labeled by: Scale" in remaining
283+
284+
# Removing non-existent tags is idempotent
285+
remaining2 = dataset.remove_tags(["nonexistent"])
286+
assert remaining2 == remaining
287+
288+
# String argument should raise TypeError
289+
with pytest.raises(TypeError):
290+
dataset.add_tags("not a list")
291+
with pytest.raises(TypeError):
292+
dataset.remove_tags("not a list")
293+
294+
260295
def test_dataset_append_local(CLIENT, dataset):
261296
ds_items_local_error = [
262297
DatasetItem(

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL