@@ -241,9 +241,18 @@ def application(self, application_id: str) -> Application:
241241
242242 Raises:
243243 NotFoundException: If the application with the given ID is not found.
244- Exception : If the application cannot be retrieved.
244+ RuntimeError : If the application cannot be retrieved unexpectedly .
245245 """
246- return self ._get_platform_client ().application (application_id )
246+ try :
247+ return self ._get_platform_client ().application (application_id )
248+ except NotFoundException as e :
249+ message = f"Application with ID '{ application_id } ' not found: { e } "
250+ logger .warning (message )
251+ raise NotFoundException (message ) from e
252+ except Exception as e :
253+ message = f"Failed to retrieve application with ID '{ application_id } ': { e } "
254+ logger .exception (message )
255+ raise RuntimeError (message ) from e
247256
248257 def application_version (
249258 self , application_version_id : str , use_latest_if_no_version_given : bool = False
@@ -260,7 +269,7 @@ def application_version(
260269 Raises:
261270 ValueError: If the application version ID is invalid.
262271 NotFoundException: If the application with the given ID is not found.
263- Exception : If the application cannot be retrieved.
272+ RuntimeError : If the application cannot be retrieved unexpectedly .
264273 """
265274 # Validate format: application_id:vX.Y.Z (where X.Y.Z is a semver)
266275 # This checks for proper format like "he-tme:v0.50.0" where "he-tme" is the application id
@@ -299,9 +308,19 @@ def application_versions(self, application: Application) -> list[ApplicationVers
299308 list[ApplicationVersion]: A list of all application versions.
300309
301310 Raises:
302- Exception: If version list cannot be retrieved
311+ NotFoundException: If the application with the given ID is not found.
312+ RuntimeError: If version list cannot be retrieved unexpectedly.
303313 """
304- return self ._get_platform_client ().applications .versions .list_sorted (application = application )
314+ try :
315+ return self ._get_platform_client ().applications .versions .list_sorted (application = application )
316+ except NotFoundException as e :
317+ message = f"Application with ID '{ application .application_id } ' not found: { e } "
318+ logger .warning (message )
319+ raise NotFoundException (message ) from e
320+ except Exception as e :
321+ message = f"Failed to retrieve application versions for application '{ application .application_id } ': { e } "
322+ logger .exception (message )
323+ raise RuntimeError (message ) from e
305324
306325 def application_version_latest (self , application : Application ) -> ApplicationVersion | None :
307326 """Get a latest application version.
@@ -313,7 +332,8 @@ def application_version_latest(self, application: Application) -> ApplicationVer
313332 ApplicationVersion | None: A list of all application versions.
314333
315334 Raises:
316- Exception: If version list cannot be retrieved
335+ NotFoundException: If the application with the given ID is not found.
336+ RuntimeError: If version list cannot be retrieved unexpectedly.
317337 """
318338 versions = self .application_versions (application )
319339 return versions [0 ] if versions else None
@@ -403,12 +423,14 @@ def generate_metadata_from_source_directory(
403423 Exception: If the metadata cannot be generated.
404424
405425 Raises:
426+ NotFoundError: If the application version with the given ID is not found.
406427 ValueError: If the source directory does not exist or is not a directory.
428+ RuntimeError: If the metadata generation fails unexpectedly.
407429 """
408430 logger .debug ("Generating metadata from source directory: %s" , source_directory )
409431
410432 # TODO(Helmut): Use it
411- application_version = Service ().application_version (application_version_id , use_latest_if_no_version_given = True ) # noqa: F841
433+ _ = Service ().application_version (application_version_id , use_latest_if_no_version_given = True )
412434
413435 metadata = []
414436
@@ -453,17 +475,18 @@ def generate_metadata_from_source_directory(
453475 Service ._apply_mappings_to_entry (entry , mappings )
454476
455477 metadata .append (entry )
456- except Exception :
457- message = f"Failed to process file '{ file_path } '"
458- logger .exception (message )
478+ except Exception as e : # noqa: BLE001
479+ message = f"Failed to process file '{ file_path } ': { e } "
480+ logger .warning (message )
459481 continue
460482
461483 logger .debug ("Generated metadata for %d files" , len (metadata ))
462484 return metadata
463485
464- except Exception :
465- logger .exception ("Failed to generate metadata from source directory: %s" , source_directory )
466- raise
486+ except Exception as e :
487+ message = f"Failed to generate metadata from source directory '{ source_directory } ': { e } "
488+ logger .exception (message )
489+ raise RuntimeError (message ) from e
467490
468491 @staticmethod
469492 def application_run_upload (
@@ -485,6 +508,11 @@ def application_run_upload(
485508
486509 Returns:
487510 bool: True if the upload was successful, False otherwise.
511+
512+ Raises:
513+ NotFoundException: If the application version with the given ID is not found.
514+ RuntimeError: If fetching the application version fails unexpectedly.
515+ requests.HTTPError: If the upload fails with an HTTP error.
488516 """
489517 import psutil # noqa: PLC0415
490518
@@ -555,6 +583,18 @@ def read_in_chunks( # noqa: PLR0913, PLR0917
555583
556584 @staticmethod
557585 def application_runs_static (limit : int | None = None , completed_only : bool = False ) -> list [dict [str , Any ]]:
586+ """Get a list of all application runs, static variant.
587+
588+ Args:
589+ limit (int | None): The maximum number of runs to retrieve. If None, all runs are retrieved.
590+ completed_only (bool): If True, only completed runs are retrieved.
591+
592+ Returns:
593+ list[ApplicationRunData]: A list of all application runs.
594+
595+ Raises:
596+ RuntimeError: If the application run list cannot be retrieved.
597+ """
558598 return [
559599 {
560600 "application_run_id" : run .application_run_id ,
@@ -580,20 +620,25 @@ def application_runs(
580620 list[ApplicationRunData]: A list of all application runs.
581621
582622 Raises:
583- Exception : If the application run list cannot be retrieved.
623+ RuntimeError : If the application run list cannot be retrieved.
584624 """
585625 if limit is not None and limit <= 0 :
586626 return []
587627 runs = []
588628 page_size = LIST_APPLICATION_RUNS_MAX_PAGE_SIZE
589- run_iterator = self ._get_platform_client ().runs .list_data (sort = "-triggered_at" , page_size = page_size )
590- for run in run_iterator :
591- if status is not None and run .status != status :
592- continue
593- runs .append (run )
594- if limit is not None and len (runs ) >= limit :
595- break
596- return runs
629+ try :
630+ run_iterator = self ._get_platform_client ().runs .list_data (sort = "-triggered_at" , page_size = page_size )
631+ for run in run_iterator :
632+ if status is not None and run .status != status :
633+ continue
634+ runs .append (run )
635+ if limit is not None and len (runs ) >= limit :
636+ break
637+ return runs
638+ except Exception as e :
639+ message = f"Failed to retrieve application runs: { e } "
640+ logger .exception (message )
641+ raise RuntimeError (message ) from e
597642
598643 def application_run (self , run_id : str ) -> ApplicationRun :
599644 """Find a run by its ID.
@@ -605,9 +650,14 @@ def application_run(self, run_id: str) -> ApplicationRun:
605650 ApplicationRun: The run that can be fetched using the .details() call.
606651
607652 Raises:
608- Exception : If initializing the client fails.
653+ RuntimeError : If initializing the client fails or the run cannot be retrieved .
609654 """
610- return self ._get_platform_client ().run (run_id )
655+ try :
656+ return self ._get_platform_client ().run (run_id )
657+ except Exception as e :
658+ message = f"Failed to retrieve application run with ID '{ run_id } ': { e } "
659+ logger .exception (message )
660+ raise RuntimeError (message ) from e
611661
612662 def application_run_submit_from_metadata (
613663 self , application_version_id : str , metadata : list [dict [str , Any ]]
@@ -623,8 +673,10 @@ def application_run_submit_from_metadata(
623673 ApplicationRun: The submitted run.
624674
625675 Raises:
626- ValueError: If platform bucket URL is missing or has unsupported protocol.
627- Exception: If submitting the run failed unexpectedly.
676+ NotFoundException: If the application version with the given ID is not found.
677+ ValueError: If platform bucket URL is missing or has unsupported protocol,
678+ or if the application version ID is invalid.
679+ RuntimeError: If submitting the run failed unexpectedly.
628680 """
629681 logger .debug ("Submitting application run with metadata: %s" , metadata )
630682 items = []
@@ -677,9 +729,14 @@ def application_run_submit_from_metadata(
677729 "Submitted application run with items: %s, application run id %s" , items , run .application_run_id
678730 )
679731 return run
680- except Exception :
681- logger .exception ("Failed to submit application run." )
682- raise
732+ except ValueError as e :
733+ message = f"Failed to submit application run for version '{ application_version_id } ': { e } "
734+ logger .warning (message )
735+ raise ValueError (message ) from e
736+ except Exception as e :
737+ message = f"Failed to submit application run for version '{ application_version_id } ': { e } "
738+ logger .exception (message )
739+ raise RuntimeError (message ) from e
683740
684741 def application_run_submit (self , application_version_id : str , items : list [InputItem ]) -> ApplicationRun :
685742 """Submit a run for the given application.
@@ -692,9 +749,20 @@ def application_run_submit(self, application_version_id: str, items: list[InputI
692749 ApplicationRun: The submitted run.
693750
694751 Raises:
695- Exception: If submitting the run failed unexpectedly.
752+ NotFoundException: If the application version with the given ID is not found.
753+ ValueError: If the application version ID is invalid or items invalid.
754+ RuntimeError: If submitting the run failed unexpectedly.
696755 """
697- return self ._get_platform_client ().runs .create (application_version = application_version_id , items = items )
756+ try :
757+ return self ._get_platform_client ().runs .create (application_version = application_version_id , items = items )
758+ except ValueError as e :
759+ message = f"Failed to submit application run for version '{ application_version_id } ': { e } "
760+ logger .warning (message )
761+ raise ValueError (message ) from e
762+ except Exception as e :
763+ message = f"Failed to submit application run for version '{ application_version_id } ': { e } "
764+ logger .exception (message )
765+ raise RuntimeError (message ) from e
698766
699767 def application_run_cancel (self , run_id : str ) -> None :
700768 """Cancel a run by its ID.
@@ -706,9 +774,24 @@ def application_run_cancel(self, run_id: str) -> None:
706774 Exception: If the client cannot be created.
707775
708776 Raises:
709- Exception: If canceling the run failed unexpectedly.
777+ NotFoundException: If the application run with the given ID is not found.
778+ ValueError: If the run ID is invalid or the run cannot be canceled given its current status.
779+ RuntimeError: If canceling the run fails unexpectedly.
710780 """
711- self .application_run (run_id ).cancel ()
781+ try :
782+ self .application_run (run_id ).cancel ()
783+ except ValueError as e :
784+ message = f"Failed to cancel application run with ID '{ run_id } ': ValueError { e } "
785+ logger .warning (message )
786+ raise ValueError (message ) from e
787+ except NotFoundException as e :
788+ message = f"Application run with ID '{ run_id } ' not found: { e } "
789+ logger .warning (message )
790+ raise NotFoundException (message ) from e
791+ except Exception as e :
792+ message = f"Failed to cancel application run with ID '{ run_id } ': { e } "
793+ logger .exception (message )
794+ raise RuntimeError (message ) from e
712795
713796 @staticmethod
714797 def application_run_download_static ( # noqa: PLR0913, PLR0917
@@ -720,6 +803,30 @@ def application_run_download_static( # noqa: PLR0913, PLR0917
720803 qupath_project : bool = False ,
721804 download_progress_queue : Any | None = None , # noqa: ANN401
722805 ) -> Path :
806+ """Download application run results with progress tracking, static variant.
807+
808+ Args:
809+ run_id (str): The ID of the application run to download.
810+ destination_directory (Path): Directory to save downloaded files.
811+ create_subdirectory_for_run (bool): Whether to create a subdirectory for the run.
812+ create_subdirectory_per_item (bool): Whether to create a subdirectory for each item,
813+ if not set, all items will be downloaded to the same directory but prefixed
814+ with the item reference and underscore.
815+ wait_for_completion (bool): Whether to wait for run completion. Defaults to True.
816+ qupath_project (bool): If True, create QuPath project referencing input slides and results.
817+ This requires QuPath to be installed. The QuPath project will be created in a subfolder
818+ of the destination directory.
819+ download_progress_queue (Queue | None): Queue for GUI progress updates.
820+
821+ Returns:
822+ Path: The directory containing downloaded results.
823+
824+ Raises:
825+ ValueError: If the run ID is invalid or destination directory cannot be created.
826+ NotFoundException: If the application run with the given ID is not found.
827+ RuntimeError: If run details cannot be retrieved or download fails unexpectedly.
828+ requests.HTTPError: If the download fails with an HTTP error.
829+ """
723830 return Service ().application_run_download (
724831 run_id ,
725832 destination_directory ,
@@ -763,8 +870,9 @@ def application_run_download( # noqa: C901, PLR0912, PLR0913, PLR0914, PLR0915,
763870
764871 Raises:
765872 ValueError: If the run ID is invalid or destination directory cannot be created.
766- RuntimeError: If run details cannot be retrieved or download fails.
767- Exception: If run cannot be retrieved, destination directory cannot be created, or download fails.
873+ NotFoundException: If the application run with the given ID is not found.
874+ RuntimeError: If run details cannot be retrieved or download fails unexpectedly.
875+ requests.HTTPError: If the download fails with an HTTP error.
768876 """
769877 if qupath_project and not has_qupath_extra :
770878 message = "QuPath project creation requested, but 'qupath' extra is not installed."
@@ -778,6 +886,10 @@ def application_run_download( # noqa: C901, PLR0912, PLR0913, PLR0914, PLR0915,
778886 final_destination_directory = destination_directory
779887 try :
780888 details = application_run .details ()
889+ except NotFoundException as e :
890+ message = f"Application run with ID '{ run_id } ' not found: { e } "
891+ logger .warning (message )
892+ raise NotFoundException (message ) from e
781893 except ApiException as e :
782894 if e .status == HTTPStatus .UNPROCESSABLE_ENTITY : # Don't use UNPROCESSABLE_CONTENT
783895 message = f"Run ID '{ run_id } ' invalid: { e !s} ."
0 commit comments