@@ -82,7 +82,7 @@ def _determine_file_name(filename, outdir, label, extension, gzip):
8282 return result_file_name (outdir , label , extension , gzip )
8383
8484
85- def read_in_result (filename = None , outdir = None , label = None , extension = 'json' , gzip = False , result_class = None ):
85+ def read_in_result (filename = None , outdir = None , label = None , extension = None , gzip = False , result_class = None ):
8686 """ Reads in a stored bilby result object
8787
8888 Parameters
@@ -92,33 +92,67 @@ def read_in_result(filename=None, outdir=None, label=None, extension='json', gzi
9292 outdir, label, extension: str
9393 Name of the output directory, label and extension used for the default
9494 naming scheme.
95+ extension: str, optional
96+ The file extension to use. If not given, the extension is inferred from
97+ the filename if provided. If the filename is not given, defaults to
98+ 'json'.
9599 result_class: bilby.core.result.Result, or child of
96100 The result class to use. By default, `bilby.core.result.Result` is used,
97101 but objects which inherit from this class can be given providing
98102 additional methods.
99103 """
100- filename = _determine_file_name (filename , outdir , label , extension , gzip )
104+ filename = _determine_file_name (
105+ filename , outdir , label , extension or "json" , gzip
106+ )
101107
102108 if result_class is None :
103109 result_class = Result
104110 elif not issubclass (result_class , Result ):
105111 raise ValueError (f"Input result_class={ result_class } not understood" )
106112
107113 # Get the actual extension (may differ from the default extension if the filename is given)
108- extension = os .path .splitext (filename )[1 ].lstrip ('.' )
114+ if extension is None :
115+ ext = os .path .splitext (filename )[1 ][1 :]
116+ extension = ext if ext else None
109117 if extension == 'gz' : # gzipped file
110118 extension = os .path .splitext (os .path .splitext (filename )[0 ])[1 ].lstrip ('.' )
111119
112- if 'json' in extension :
113- result = result_class .from_json (filename = filename )
114- elif ('hdf5' in extension ) or ('h5' in extension ):
115- result = result_class .from_hdf5 (filename = filename )
116- elif ("pkl" in extension ) or ("pickle" in extension ):
117- result = result_class .from_pickle (filename = filename )
118- elif extension is None :
119- raise ValueError ("No filetype extension provided" )
120- else :
121- raise ValueError ("Filetype {} not understood" .format (extension ))
120+ if extension is None :
121+ raise ValueError ("No filetype extension provided and could not be inferred from filename" )
122+
123+ extension = extension .lower ()
124+ read_functions = {
125+ 'json' : result_class .from_json ,
126+ 'hdf5' : result_class .from_hdf5 ,
127+ 'h5' : result_class .from_hdf5 ,
128+ 'pkl' : result_class .from_pickle ,
129+ 'pickle' : result_class .from_pickle ,
130+ }
131+ if extension not in read_functions :
132+ raise ValueError (
133+ f"Filetype { extension } not understood, known types are { list (read_functions .keys ())} "
134+ )
135+
136+ func = read_functions [extension ]
137+
138+ # Raise IO errors since this is what the caching relies on
139+ # Catch all other exceptions and raise a FileLoadError
140+ try :
141+ result = func (filename = filename )
142+ except IOError as e :
143+ raise IOError (
144+ f"Failed to read in file { filename } using "
145+ f"`{ result_class .__name__ } .{ func .__name__ } ` "
146+ f"(extension={ extension } ). "
147+ "This is likely because the file does not exist or is not a valid bilby result."
148+ f"The error was: { e } "
149+ ) from e
150+ except Exception as e :
151+ raise FileLoadError (
152+ f"Failed to read in file { filename } using "
153+ f"`{ result_class .__name__ } .{ func .__name__ } ` "
154+ f"(extension={ extension } ). The error was: { e } "
155+ ) from e
122156 return result
123157
124158
@@ -777,7 +811,8 @@ def save_to_file(self, filename=None, overwrite=False, outdir=None,
777811 Parameters
778812 ==========
779813 filename: optional,
780- Filename to write to (overwrites the default)
814+ Filename to write to (overwrites the default). Assumed to include the
815+ file extension.
781816 overwrite: bool, optional
782817 Whether or not to overwrite an existing result file.
783818 default=False
@@ -794,24 +829,39 @@ def save_to_file(self, filename=None, overwrite=False, outdir=None,
794829
795830 _outdir = None
796831 if filename is not None :
797- _outdir , base_filename = os .path .split (filename )
832+ # Overwrite the full filename with the base filename
833+ # Well append the outdir later on
834+ _outdir , filename = os .path .split (filename )
798835 _outdir = None if _outdir == "" else _outdir
799- base , ext = os .path .splitext (base_filename )
800- if extension is None :
801- extension = ext [1 :] if ext else "json"
802- filename = base_filename
803- elif extension is True :
804- message = "Result.save_to_file called with extension=True. "
805- if len (ext ) > 0 :
806- message += f"Overwriting extension to json from { ext } , this"
807- else :
808- message += "This"
809- message += " behaviour is deprecated and will be removed."
810- logger .warning (message )
811- extension = "json"
812- filename = base_filename
813- filename = f"{ base } .{ extension } "
814- if extension is None or extension is True :
836+ _ , ext = os .path .splitext (filename )
837+ ext = ext [1 :] if ext else None
838+ # If the extension has not been set, try to infer it from the filename
839+ # if not, it will fall back to the default
840+ if ext in EXTENSIONS :
841+ if extension is None :
842+ logger .debug (
843+ f"Inferred extension '{ ext } ' from filename '{ filename } '. "
844+ "Using this extension for saving."
845+ )
846+ extension = ext
847+ elif ext != extension :
848+ message = (
849+ f"The specified extension '{ ext } ' "
850+ f"does not match the provided extension '{ extension } '. "
851+ )
852+ logger .warning (message )
853+
854+ if extension is None :
855+ logger .info ("No extension given, defaulting to JSON." )
856+ extension = 'json'
857+
858+ if extension is True :
859+ message = (
860+ "Result.save_to_file called with extension=True. "
861+ "This will default to json, and ignore the extension from the filename. "
862+ "This behaviour is deprecated and will be removed. "
863+ )
864+ logger .warning (message )
815865 extension = 'json'
816866
817867 outdir = _outdir if outdir is None else outdir
@@ -845,13 +895,13 @@ def save_to_file(self, filename=None, overwrite=False, outdir=None,
845895 else :
846896 with open (output_path , 'w' ) as file :
847897 json .dump (dictionary , file , indent = 2 , cls = BilbyJsonEncoder )
848- elif extension == 'hdf5' :
898+ elif extension in [ 'hdf5' , 'h5' ] :
849899 import h5py
850900 dictionary ["__module__" ] = self .__module__
851901 dictionary ["__name__" ] = self .__class__ .__name__
852902 with h5py .File (output_path , 'w' ) as h5file :
853903 recursively_save_dict_contents_to_group (h5file , '/' , dictionary )
854- elif extension == 'pkl' :
904+ elif extension in [ 'pkl' , 'pickle' ] :
855905 safe_file_dump (self , output_path , "dill" )
856906 else :
857907 raise ValueError (f"Extension type { extension } not understood" )
@@ -2291,3 +2341,7 @@ class ResultListError(ResultError):
22912341
22922342class FileMovedError (ResultError ):
22932343 """ Exceptions that occur when files have been moved """
2344+
2345+
2346+ class FileLoadError (ResultError ):
2347+ """ Exceptions that occur when files cannot be loaded """
0 commit comments