Skip to content

Commit 877258f

Browse files
davidnichols-opsdavidnichols-ops
authored andcommitted
fix: return single_upload result from Project.upload() (#254)
Project.upload() discarded the return value of single_upload(), returning None even on success. This made it impossible for callers to inspect the upload response (image id, timing, retry counts) without calling single_upload() directly. Now returns the single_upload() result dict for single-file uploads, and a list of such dicts for directory uploads. Existing callers that ignore the return value are unaffected.
1 parent 07cb3d1 commit 877258f

3 files changed

Lines changed: 55 additions & 3 deletions

File tree

‎roboflow/core/project.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ def upload(
409409
metadata (dict, optional): custom key-value metadata to attach to the image.
410410
Example: {"camera_id": "cam001", "location": "warehouse"}
411411
412+
Returns:
413+
For a single image: the dict returned by ``single_upload`` (keys: ``image``,
414+
``annotation``, ``upload_time``, ``annotation_time``, ``upload_retry_attempts``,
415+
``annotation_upload_retry_attempts``). For a directory: a list of such dicts,
416+
one per successfully uploaded image. Skipped (non-image) files are excluded.
417+
412418
Example:
413419
>>> import roboflow
414420
@@ -445,7 +451,7 @@ def upload(
445451
)
446452
)
447453

448-
self.single_upload(
454+
return self.single_upload(
449455
image_path=image_path,
450456
annotation_path=annotation_path,
451457
hosted_image=hosted_image,
@@ -460,11 +466,12 @@ def upload(
460466
)
461467

462468
else:
469+
results = []
463470
images = os.listdir(image_path)
464471
for image in images:
465472
path = image_path + "/" + image
466473
if self.check_valid_image(path):
467-
self.single_upload(
474+
result = self.single_upload(
468475
image_path=path,
469476
annotation_path=annotation_path,
470477
hosted_image=hosted_image,
@@ -477,10 +484,12 @@ def upload(
477484
metadata=metadata,
478485
**kwargs,
479486
)
487+
results.append(result)
480488
print("[ " + path + " ] was uploaded succesfully.")
481489
else:
482490
print("[ " + path + " ] was skipped.")
483491
continue
492+
return results
484493

485494
def upload_image(
486495
self,

‎tests/test_project.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import os
23
from unittest.mock import patch
34

45
import requests
@@ -155,6 +156,47 @@ def test_upload_raises_upload_annotation_error(self):
155156

156157
self.assertEqual(str(error.exception), "Image was already annotated.")
157158

159+
def test_upload_single_file_returns_result(self):
160+
"""upload() should return the single_upload result dict for a single file (#254)."""
161+
image_id = "test-upload-id"
162+
163+
responses.add(
164+
responses.POST,
165+
f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}&batch={DEFAULT_BATCH_NAME}",
166+
json={"success": True, "id": image_id},
167+
status=200,
168+
)
169+
170+
result = self.project.upload("tests/images/rabbit.JPG")
171+
172+
self.assertIsInstance(result, dict)
173+
self.assertEqual(result["image"]["id"], image_id)
174+
self.assertIn("upload_time", result)
175+
self.assertIn("upload_retry_attempts", result)
176+
177+
def test_upload_directory_returns_list_of_results(self):
178+
"""upload() should return a list of single_upload results for a directory (#254)."""
179+
test_dir = "tests/images"
180+
# Determine how many valid images are in the directory so we can mock
181+
# exactly that many upload responses.
182+
valid_images = [f for f in os.listdir(test_dir) if self.project.check_valid_image(os.path.join(test_dir, f))]
183+
184+
for i, _ in enumerate(valid_images):
185+
responses.add(
186+
responses.POST,
187+
f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}&batch={DEFAULT_BATCH_NAME}",
188+
json={"success": True, "id": f"img-{i}"},
189+
status=200,
190+
)
191+
192+
result = self.project.upload(test_dir)
193+
194+
self.assertIsInstance(result, list)
195+
self.assertEqual(len(result), len(valid_images))
196+
for i, entry in enumerate(result):
197+
self.assertIsInstance(entry, dict)
198+
self.assertEqual(entry["image"]["id"], f"img-{i}")
199+
158200
def test_image_success(self):
159201
image_id = "test-image-id"
160202
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"

‎tests/test_queries.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ def test_project_methods(self):
6060
self.assertEqual(len(version_information), 2)
6161
self.assertIsNone(print_versions)
6262
self.assertTrue(all(map(lambda x: isinstance(x, Version), list_versions)))
63-
self.assertIsNone(upload)
63+
self.assertIsInstance(upload, dict)
64+
self.assertEqual(upload["image"]["id"], "hbALkCFdNr9rssgOUXug")
6465

6566
@ordered
6667
def test_version_fields(self):

0 commit comments

Comments
 (0)