1313from types import FunctionType
1414from warnings import filterwarnings , warn
1515from distutils .util import strtobool
16+ from time import sleep
1617
1718import click
1819import pynwb
2829)
2930from .register_checks import InspectorMessage , Importance
3031from .tools import get_s3_urls_and_dandi_paths
31- from .utils import FilePathType , PathType , OptionalListOfStrings
32+ from .utils import FilePathType , PathType , OptionalListOfStrings , robust_s3_read
3233
3334INTERNAL_CONFIGS = dict (dandi = Path (__file__ ).parent / "internal_configs" / "dandi.inspector_config.yaml" )
3435
@@ -414,8 +415,9 @@ def inspect_nwb(
414415 ignore : OptionalListOfStrings = None ,
415416 select : OptionalListOfStrings = None ,
416417 importance_threshold : Union [str , Importance ] = Importance .BEST_PRACTICE_SUGGESTION ,
417- driver : str = None ,
418+ driver : Optional [ str ] = None ,
418419 skip_validate : bool = False ,
420+ max_retries : int = 10 ,
419421) -> List [InspectorMessage ]:
420422 """
421423 Inspect a NWBFile object and return suggestions for improvements according to best practices.
@@ -449,6 +451,11 @@ def inspect_nwb(
449451 skip_validate : bool
450452 Skip the PyNWB validation step. This may be desired for older NWBFiles (< schema version v2.10).
451453 The default is False, which is also recommended.
454+ max_retries : int, optional
455+ When using the ros3 driver to stream data from an s3 path, occasional curl issues can result.
456+ AWS suggests using iterative retry with an exponential backoff of 0.1 * 2^retries.
457+ This sets a hard bound on the number of times to attempt to retry the collection of messages.
458+ Defaults to 10 (corresponds to 102.4s maximum delay on final attempt).
452459 """
453460 importance_threshold = (
454461 Importance [importance_threshold ] if isinstance (importance_threshold , str ) else importance_threshold
@@ -460,6 +467,7 @@ def inspect_nwb(
460467 nwbfile_path = str (nwbfile_path )
461468 filterwarnings (action = "ignore" , message = "No cached namespaces found in .*" )
462469 filterwarnings (action = "ignore" , message = "Ignoring cached namespace .*" )
470+
463471 with pynwb .NWBHDF5IO (path = nwbfile_path , mode = "r" , load_namespaces = True , driver = driver ) as io :
464472 if not skip_validate :
465473 validation_errors = pynwb .validate (io = io )
@@ -473,7 +481,7 @@ def inspect_nwb(
473481 )
474482
475483 try :
476- nwbfile = io .read ( )
484+ nwbfile = robust_s3_read ( command = io .read , max_retries = max_retries )
477485 for inspector_message in run_checks (nwbfile = nwbfile , checks = checks ):
478486 inspector_message .file_path = nwbfile_path
479487 yield inspector_message
@@ -499,7 +507,7 @@ def run_checks(nwbfile: pynwb.NWBFile, checks: list):
499507 for nwbfile_object in nwbfile .objects .values ():
500508 if check_function .neurodata_type is None or issubclass (type (nwbfile_object ), check_function .neurodata_type ):
501509 try :
502- output = check_function ( nwbfile_object )
510+ output = robust_s3_read ( command = check_function , command_args = [ nwbfile_object ] )
503511 # if an individual check fails, include it in the report and continue with the inspection
504512 except Exception :
505513 output = InspectorMessage (
0 commit comments