From ad9c94d3a2b7f444bdf837199de40d7ee8de698c Mon Sep 17 00:00:00 2001 From: Matthew Roberson Date: Thu, 9 Jul 2026 17:42:51 -0500 Subject: [PATCH 1/3] [PLT-0] Fix test expectations for updated test assets Follow-up to the test-asset repath (#2055). The new hosted assets differ from the deleted originals, so several tests need their fixtures/expected values realigned: - image_url: point at a stable, byte-stable JPEG (potato.jpeg) instead of picsum.photos, which returns a random image per request and broke every byte-equality assertion in test_data_rows.py. This also restores test_multiple_chunks (the shorter picsum URL under-filled the mocked 300-byte upsert chunks, yielding 7 upload calls instead of 11). - Same picsum -> stable JPEG swap in the alignerr conftest (latent; no byte-equality asserts there, but removes the third-party dependency). - Text TextEntity token updated to match sample-text.txt at offsets 112-128. - Document entity token IDs/groupId/text and page width (594) updated to match sample-pdf-textlayer.json / test-pdf.pdf. - Video frame_rate/frame_count updated to test_video_500kb.mp4 (25 fps, 627). Co-Authored-By: Claude Opus 4.8 (1M context) --- libs/labelbox/tests/conftest.py | 5 ++- .../tests/data/annotation_import/conftest.py | 44 ++++++++++--------- .../test_export_video_streamable.py | 8 ++-- libs/lbox-alignerr/tests/conftest.py | 4 +- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/libs/labelbox/tests/conftest.py b/libs/labelbox/tests/conftest.py index 42bb5e5e8..87d59bdb1 100644 --- a/libs/labelbox/tests/conftest.py +++ b/libs/labelbox/tests/conftest.py @@ -33,7 +33,10 @@ from labelbox.schema.project import Project from labelbox.schema.quality_mode import QualityMode -IMG_URL = "https://picsum.photos/200/300.jpg" +# Must be a stable, deterministic JPEG: several tests assert byte-equality +# between the source and the server-rehosted copy, so a random image service +# (e.g. picsum.photos) or a format the server may transcode (e.g. PNG) breaks them. +IMG_URL = "https://storage.googleapis.com/lb-artifacts-testing-public/sdk_integration_test/potato.jpeg" SMALL_DATASET_URL = "https://storage.googleapis.com/lb-artifacts-testing-public/sdk_integration_test/potato.jpeg" DATA_ROW_PROCESSING_WAIT_TIMEOUT_SECONDS = 30 DATA_ROW_PROCESSING_WAIT_SLEEP_INTERNAL_SECONDS = 3 diff --git a/libs/labelbox/tests/data/annotation_import/conftest.py b/libs/labelbox/tests/data/annotation_import/conftest.py index c3f574b2c..5be0094b6 100644 --- a/libs/labelbox/tests/data/annotation_import/conftest.py +++ b/libs/labelbox/tests/data/annotation_import/conftest.py @@ -1280,16 +1280,17 @@ def entity_inference_document(prediction_id_mapping): "textSelections": [ { "tokenIds": [ - "3f984bf3-1d61-44f5-b59a-9658a2e3440f", - "3bf00b56-ff12-4e52-8cc1-08dbddb3c3b8", - "6e1c3420-d4b7-4c5a-8fd6-ead43bf73d80", - "87a43d32-af76-4a1d-b262-5c5f4d5ace3a", - "e8606e8a-dfd9-4c49-a635-ad5c879c75d0", - "67c7c19e-4654-425d-bf17-2adb8cf02c30", - "149c5e80-3e07-49a7-ab2d-29ddfe6a38fa", - "b0e94071-2187-461e-8e76-96c58738a52c", + "543ea587-6817-4d2b-a9c1-6d8dd797e8d7", + "f4c15744-c9fe-48f2-89dc-8f52fafda028", + "43867d16-0236-47cd-b348-5190143a3ccb", + "521f705e-b276-4ac7-8e5b-2e38e037f80f", + "4c1475f9-3576-4171-91a9-7cfb369c73d8", + "a75658b1-dd91-4f9f-b7d8-572eee7fd707", + "480a3484-28d9-4690-a466-0b8e24a24c3a", + "d8fcd3cb-4bf7-461c-a91f-f9142b6bc3c3", + "4ec0bac5-e5d7-4eda-802b-3962ad2db82c", ], - "groupId": "2f4336f4-a07e-4e0a-a9e1-5629b03b719b", + "groupId": "ed53dd86-ef39-4634-9505-ee0eebedef44", "page": 1, } ] @@ -2155,7 +2156,7 @@ def expected_export_v2_text(): "location": { "start": 112, "end": 128, - "token": "research suggests", + "token": " sollicitudin nis", }, } ], @@ -2350,19 +2351,20 @@ def expected_export_v2_document(): "location": { "groups": [ { - "id": "2f4336f4-a07e-4e0a-a9e1-5629b03b719b", + "id": "ed53dd86-ef39-4634-9505-ee0eebedef44", "page_number": 1, "tokens": [ - "3f984bf3-1d61-44f5-b59a-9658a2e3440f", - "3bf00b56-ff12-4e52-8cc1-08dbddb3c3b8", - "6e1c3420-d4b7-4c5a-8fd6-ead43bf73d80", - "87a43d32-af76-4a1d-b262-5c5f4d5ace3a", - "e8606e8a-dfd9-4c49-a635-ad5c879c75d0", - "67c7c19e-4654-425d-bf17-2adb8cf02c30", - "149c5e80-3e07-49a7-ab2d-29ddfe6a38fa", - "b0e94071-2187-461e-8e76-96c58738a52c", + "543ea587-6817-4d2b-a9c1-6d8dd797e8d7", + "f4c15744-c9fe-48f2-89dc-8f52fafda028", + "43867d16-0236-47cd-b348-5190143a3ccb", + "521f705e-b276-4ac7-8e5b-2e38e037f80f", + "4c1475f9-3576-4171-91a9-7cfb369c73d8", + "a75658b1-dd91-4f9f-b7d8-572eee7fd707", + "480a3484-28d9-4690-a466-0b8e24a24c3a", + "d8fcd3cb-4bf7-461c-a91f-f9142b6bc3c3", + "4ec0bac5-e5d7-4eda-802b-3962ad2db82c", ], - "text": "Metal-insulator (MI) transitions have been one of the", + "text": "Mott transition and superconductivity in the strongly correlated organic", } ] }, @@ -2382,7 +2384,7 @@ def expected_export_v2_document(): }, "page_dimensions": { "height": 792.0, - "width": 612.0, + "width": 594.0, "unit": "points", }, }, diff --git a/libs/labelbox/tests/data/export/streamable/test_export_video_streamable.py b/libs/labelbox/tests/data/export/streamable/test_export_video_streamable.py index eec56ff51..efbd76774 100644 --- a/libs/labelbox/tests/data/export/streamable/test_export_video_streamable.py +++ b/libs/labelbox/tests/data/export/streamable/test_export_video_streamable.py @@ -76,11 +76,11 @@ def test_export( assert data_row_export["row_data"] == video_data_row["row_data"] assert export_data["media_attributes"]["mime_type"] == "video/mp4" assert ( - export_data["media_attributes"]["frame_rate"] == 10 - ) # as per the video_data fixture + export_data["media_attributes"]["frame_rate"] == 25 + ) # intrinsic to test_video_500kb.mp4 assert ( - export_data["media_attributes"]["frame_count"] == 100 - ) # as per the video_data fixture + export_data["media_attributes"]["frame_count"] == 627 + ) # intrinsic to test_video_500kb.mp4 (25 fps x 25.08s) expected_export_label = { "label_kind": "Video", "version": "1.0.0", diff --git a/libs/lbox-alignerr/tests/conftest.py b/libs/lbox-alignerr/tests/conftest.py index 5368b164e..fa6f4d9a0 100644 --- a/libs/lbox-alignerr/tests/conftest.py +++ b/libs/lbox-alignerr/tests/conftest.py @@ -33,7 +33,9 @@ from labelbox.schema.project import Project from labelbox.schema.quality_mode import QualityMode -IMG_URL = "https://picsum.photos/200/300.jpg" +# Use a stable, GCS-hosted JPEG rather than a random third-party image service +# so data-row creation does not depend on picsum.photos availability. +IMG_URL = "https://storage.googleapis.com/lb-artifacts-testing-public/sdk_integration_test/potato.jpeg" SMALL_DATASET_URL = "https://storage.googleapis.com/lb-artifacts-testing-public/sdk_integration_test/potato.jpeg" DATA_ROW_PROCESSING_WAIT_TIMEOUT_SECONDS = 30 DATA_ROW_PROCESSING_WAIT_SLEEP_INTERNAL_SECONDS = 3 From 81f92d53de5eac834a60da90333266c261ffa97f Mon Sep 17 00:00:00 2001 From: Matthew Roberson Date: Thu, 9 Jul 2026 18:06:41 -0500 Subject: [PATCH 2/3] Cap pytest <9 to fix test collection CI installs deps via `rye sync -f --update-all`, which ignores the lockfile and resolves `pytest>=8.1.1` to pytest 9.1.1. pytest 9 changed the internal IdMaker signature, which pytest-cases (<=3.10.1) still calls the old way at import time, raising `TypeError: IdMaker.__init__() takes 7 positional arguments but 8 were given` and breaking collection in every test job. Cap pytest <9 in the root and alignerr dev-dependencies until pytest-cases ships pytest 9 support. Co-Authored-By: Claude Opus 4.8 (1M context) --- libs/lbox-alignerr/pyproject.toml | 5 ++++- pyproject.toml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libs/lbox-alignerr/pyproject.toml b/libs/lbox-alignerr/pyproject.toml index 375d488c8..5c72fc408 100644 --- a/libs/lbox-alignerr/pyproject.toml +++ b/libs/lbox-alignerr/pyproject.toml @@ -46,7 +46,10 @@ build-backend = "hatchling.build" [tool.rye] managed = true dev-dependencies = [ - "pytest>=8.1.1", + # Cap <9: pytest 9 changed the internal IdMaker signature, which pytest-cases + # (<=3.10.1) still calls the old way at import time, breaking test collection. + # Remove the cap once pytest-cases supports pytest 9. + "pytest>=8.1.1,<9", "pytest-cases>=3.8.4", "pytest-rerunfailures>=14.0", "pytest-snapshot>=0.9.0", diff --git a/pyproject.toml b/pyproject.toml index 6fa5dde92..9ddedde2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,10 @@ requires-python = ">= 3.9" managed = true virtual = true dev-dependencies = [ - "pytest>=8.1.1", + # Cap <9: pytest 9 changed the internal IdMaker signature, which pytest-cases + # (<=3.10.1) still calls the old way at import time, breaking test collection. + # Remove the cap once pytest-cases supports pytest 9. + "pytest>=8.1.1,<9", "pytest-cases>=3.8.4", "pytest-rerunfailures>=14.0", "pytest-snapshot>=0.9.0", From c778ba7dac3de04ccd52fde38661bddbf04001ed Mon Sep 17 00:00:00 2001 From: Matthew Roberson Date: Thu, 9 Jul 2026 18:06:41 -0500 Subject: [PATCH 3/3] Apply ruff format to fix pre-existing lint drift project.py, test_batch.py, and test_project.py (last touched by #2060) were not formatted per the repo's ruff config, failing `rye fmt --check` on every PR. Formatted with ruff 0.8.2 (the version bundled by CI's rye 0.43.0). Co-Authored-By: Claude Opus 4.8 (1M context) --- libs/labelbox/src/labelbox/schema/project.py | 6 +++--- libs/labelbox/tests/integration/test_batch.py | 5 +---- libs/labelbox/tests/unit/test_project.py | 17 +++++++++++++---- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libs/labelbox/src/labelbox/schema/project.py b/libs/labelbox/src/labelbox/schema/project.py index 8edde794c..1fab9aaee 100644 --- a/libs/labelbox/src/labelbox/schema/project.py +++ b/libs/labelbox/src/labelbox/schema/project.py @@ -1825,9 +1825,9 @@ def get_overview( variables["countInput"] = None # Must use experimental to access "issues" - result = self.client.execute( - query, variables, experimental=True - )["project"] + result = self.client.execute(query, variables, experimental=True)[ + "project" + ] # Reformat category names overview = { diff --git a/libs/labelbox/tests/integration/test_batch.py b/libs/labelbox/tests/integration/test_batch.py index 963cb6d71..b58a4609b 100644 --- a/libs/labelbox/tests/integration/test_batch.py +++ b/libs/labelbox/tests/integration/test_batch.py @@ -156,10 +156,7 @@ def test_get_overview_batch_scoped(project: Project, small_dataset: Dataset): while timeout_seconds > 0: overview_a = project.get_overview(batch_ids=[batch_a.uid]) overview_b = project.get_overview(batch_ids=[batch_b.uid]) - if ( - overview_a.total_data_rows == 1 - and overview_b.total_data_rows == 1 - ): + if overview_a.total_data_rows == 1 and overview_b.total_data_rows == 1: break timeout_seconds -= sleep_time time.sleep(sleep_time) diff --git a/libs/labelbox/tests/unit/test_project.py b/libs/labelbox/tests/unit/test_project.py index 8732f01d8..201f8e9e1 100644 --- a/libs/labelbox/tests/unit/test_project.py +++ b/libs/labelbox/tests/unit/test_project.py @@ -50,7 +50,9 @@ def _workstream_state_counts_response(): def test_get_overview_project_wide(project_entity): client = project_entity.client - client.execute.return_value = {"project": _workstream_state_counts_response()} + client.execute.return_value = { + "project": _workstream_state_counts_response() + } overview = project_entity.get_overview() @@ -71,7 +73,9 @@ def test_get_overview_project_wide(project_entity): def test_get_overview_batch_scoped(project_entity): client = project_entity.client - client.execute.return_value = {"project": _workstream_state_counts_response()} + client.execute.return_value = { + "project": _workstream_state_counts_response() + } overview = project_entity.get_overview(batch_ids=["batch-1"]) @@ -93,7 +97,10 @@ def test_get_overview_batch_scoped(project_entity): "batch_ids,expected_message", [ ([], "batch_ids filter expects a non-empty list."), - (["batch-1"] * 1001, "batch_ids filter only supports a max of 1000 items."), + ( + ["batch-1"] * 1001, + "batch_ids filter only supports a max of 1000 items.", + ), ], ) def test_validate_batch_ids_rejects_invalid(batch_ids, expected_message): @@ -102,7 +109,9 @@ def test_validate_batch_ids_rejects_invalid(batch_ids, expected_message): def test_get_overview_rejects_empty_batch_ids(project_entity): - with pytest.raises(ValueError, match="batch_ids filter expects a non-empty list."): + with pytest.raises( + ValueError, match="batch_ids filter expects a non-empty list." + ): project_entity.get_overview(batch_ids=[])