diff --git a/roboflow/core/project.py b/roboflow/core/project.py index 8cf86d94..8d4aad75 100644 --- a/roboflow/core/project.py +++ b/roboflow/core/project.py @@ -409,6 +409,13 @@ def upload( metadata (dict, optional): custom key-value metadata to attach to the image. Example: {"camera_id": "cam001", "location": "warehouse"} + Returns: + A list of result dicts (one per successfully uploaded image), regardless of + whether a single file or a directory was provided. Each dict is the return + value of ``single_upload`` (keys: ``image``, ``annotation``, ``upload_time``, + ``annotation_time``, ``upload_retry_attempts``, ``annotation_upload_retry_attempts``). + Skipped (non-image) files in directory mode are excluded from the list. + Example: >>> import roboflow @@ -445,26 +452,29 @@ def upload( ) ) - self.single_upload( - image_path=image_path, - annotation_path=annotation_path, - hosted_image=hosted_image, - image_id=image_id, - split=split, - num_retry_uploads=num_retry_uploads, - batch_name=batch_name, - tag_names=tag_names, - is_prediction=is_prediction, - metadata=metadata, - **kwargs, - ) + return [ + self.single_upload( + image_path=image_path, + annotation_path=annotation_path, + hosted_image=hosted_image, + image_id=image_id, + split=split, + num_retry_uploads=num_retry_uploads, + batch_name=batch_name, + tag_names=tag_names, + is_prediction=is_prediction, + metadata=metadata, + **kwargs, + ) + ] else: + results = [] images = os.listdir(image_path) for image in images: path = image_path + "/" + image if self.check_valid_image(path): - self.single_upload( + result = self.single_upload( image_path=path, annotation_path=annotation_path, hosted_image=hosted_image, @@ -477,10 +487,12 @@ def upload( metadata=metadata, **kwargs, ) + results.append(result) print("[ " + path + " ] was uploaded succesfully.") else: print("[ " + path + " ] was skipped.") continue + return results def upload_image( self, diff --git a/tests/test_project.py b/tests/test_project.py index 57d4d823..747dd09c 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1,4 +1,5 @@ import json +import os from unittest.mock import patch import requests @@ -155,6 +156,50 @@ def test_upload_raises_upload_annotation_error(self): self.assertEqual(str(error.exception), "Image was already annotated.") + def test_upload_single_file_returns_result(self): + """upload() should return a list with the single_upload result dict for a single file (#254).""" + image_id = "test-upload-id" + + responses.add( + responses.POST, + f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}&batch={DEFAULT_BATCH_NAME}", + json={"success": True, "id": image_id}, + status=200, + ) + + result = self.project.upload("tests/images/rabbit.JPG") + + self.assertIsInstance(result, list) + self.assertEqual(len(result), 1) + entry = result[0] + self.assertIsInstance(entry, dict) + self.assertEqual(entry["image"]["id"], image_id) + self.assertIn("upload_time", entry) + self.assertIn("upload_retry_attempts", entry) + + def test_upload_directory_returns_list_of_results(self): + """upload() should return a list of single_upload results for a directory (#254).""" + test_dir = "tests/images" + # Determine how many valid images are in the directory so we can mock + # exactly that many upload responses. + valid_images = [f for f in os.listdir(test_dir) if self.project.check_valid_image(os.path.join(test_dir, f))] + + for i, _ in enumerate(valid_images): + responses.add( + responses.POST, + f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}&batch={DEFAULT_BATCH_NAME}", + json={"success": True, "id": f"img-{i}"}, + status=200, + ) + + result = self.project.upload(test_dir) + + self.assertIsInstance(result, list) + self.assertEqual(len(result), len(valid_images)) + for i, entry in enumerate(result): + self.assertIsInstance(entry, dict) + self.assertEqual(entry["image"]["id"], f"img-{i}") + def test_image_success(self): image_id = "test-image-id" expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}" diff --git a/tests/test_queries.py b/tests/test_queries.py index 93c29179..267010ab 100644 --- a/tests/test_queries.py +++ b/tests/test_queries.py @@ -60,7 +60,9 @@ def test_project_methods(self): self.assertEqual(len(version_information), 2) self.assertIsNone(print_versions) self.assertTrue(all(map(lambda x: isinstance(x, Version), list_versions))) - self.assertIsNone(upload) + self.assertIsInstance(upload, list) + self.assertEqual(len(upload), 1) + self.assertEqual(upload[0]["image"]["id"], "hbALkCFdNr9rssgOUXug") @ordered def test_version_fields(self):