1313from types import FunctionType
1414from warnings import filterwarnings , warn
1515from distutils .util import strtobool
16+ from time import sleep
1617
1718import click
1819import pynwb
@@ -153,10 +154,7 @@ def configure_checks(
153154@click .argument ("path" )
154155@click .option ("--modules" , help = "Modules to import prior to reading the file(s)." )
155156@click .option (
156- "--report-file-path" ,
157- default = None ,
158- help = "Save path for the report file." ,
159- type = click .Path (writable = True ),
157+ "--report-file-path" , default = None , help = "Save path for the report file." , type = click .Path (writable = True ),
160158)
161159@click .option ("--overwrite" , help = "Overwrite an existing report file at the location." , is_flag = True )
162160@click .option ("--levels" , help = "Comma-separated names of InspectorMessage attributes to organize by." )
@@ -411,8 +409,9 @@ def inspect_nwb(
411409 ignore : OptionalListOfStrings = None ,
412410 select : OptionalListOfStrings = None ,
413411 importance_threshold : Importance = Importance .BEST_PRACTICE_SUGGESTION ,
414- driver : str = None ,
412+ driver : Optional [ str ] = None ,
415413 skip_validate : bool = False ,
414+ max_retries : int = 10 ,
416415) -> List [InspectorMessage ]:
417416 """
418417 Inspect a NWBFile object and return suggestions for improvements according to best practices.
@@ -446,6 +445,11 @@ def inspect_nwb(
446445 skip_validate : bool
447446 Skip the PyNWB validation step. This may be desired for older NWBFiles (< schema version v2.10).
448447 The default is False, which is also recommended.
448+ max_retries : int, optional
449+ When using the ros3 driver to stream data from an s3 path, occasional curl issues can result.
450+ AWS suggests using iterative retry with an exponential backoff of 0.1 * 2^retries.
451+ This sets a hard bound on the number of times to attempt to retry the collection of messages.
452+ Defaults to 10 (corresponds to 102.4s maximum delay on final attempt).
449453 """
450454 if any (x is not None for x in [config , ignore , select , importance_threshold ]):
451455 checks = configure_checks (
@@ -454,30 +458,44 @@ def inspect_nwb(
454458 nwbfile_path = str (nwbfile_path )
455459 filterwarnings (action = "ignore" , message = "No cached namespaces found in .*" )
456460 filterwarnings (action = "ignore" , message = "Ignoring cached namespace .*" )
457- with pynwb .NWBHDF5IO (path = nwbfile_path , mode = "r" , load_namespaces = True , driver = driver ) as io :
458- if not skip_validate :
459- validation_errors = pynwb .validate (io = io )
460- for validation_error in validation_errors :
461+
462+ def _collect_all_messages (nwbfile_path : FilePathType , driver : Optional [str ] = None , skip_validate : bool = False ):
463+ with pynwb .NWBHDF5IO (path = nwbfile_path , mode = "r" , load_namespaces = True , driver = driver ) as io :
464+ if not skip_validate :
465+ validation_errors = pynwb .validate (io = io )
466+ for validation_error in validation_errors :
467+ yield InspectorMessage (
468+ message = validation_error .reason ,
469+ importance = Importance .PYNWB_VALIDATION ,
470+ check_function_name = validation_error .name ,
471+ location = validation_error .location ,
472+ file_path = nwbfile_path ,
473+ )
474+
475+ try :
476+ nwbfile = io .read ()
477+ for inspector_message in run_checks (nwbfile = nwbfile , checks = checks ):
478+ inspector_message .file_path = nwbfile_path
479+ yield inspector_message
480+ except Exception as ex :
461481 yield InspectorMessage (
462- message = validation_error .reason ,
463- importance = Importance .PYNWB_VALIDATION ,
464- check_function_name = validation_error .name ,
465- location = validation_error .location ,
482+ message = traceback .format_exc (),
483+ importance = Importance .ERROR ,
484+ check_function_name = f"During io.read() - { type (ex )} : { str (ex )} " ,
466485 file_path = nwbfile_path ,
467486 )
468487
469- try :
470- nwbfile = io .read ()
471- for inspector_message in run_checks (nwbfile = nwbfile , checks = checks ):
472- inspector_message .file_path = nwbfile_path
473- yield inspector_message
474- except Exception as ex :
475- yield InspectorMessage (
476- message = traceback .format_exc (),
477- importance = Importance .ERROR ,
478- check_function_name = f"During io.read() - { type (ex )} : { str (ex )} " ,
479- file_path = nwbfile_path ,
480- )
488+ if driver != "ros3" :
489+ yield _collect_all_messages (nwbfile_path = nwbfile_path , driver = driver , skip_validate = skip_validate )
490+ else :
491+ retries = 0
492+
493+ while retries < max_retries :
494+ try :
495+ retries += 1
496+ yield _collect_all_messages (nwbfile_path = nwbfile_path , driver = driver , skip_validate = skip_validate )
497+ except OSError : # Cannot curl request
498+ sleep (0.1 * 2 ** retries )
481499
482500
483501def run_checks (nwbfile : pynwb .NWBFile , checks : list ):
0 commit comments