37
37
from .factory .proxy import Simvue
38
38
from .metrics import get_gpu_metrics , get_process_cpu , get_process_memory
39
39
from .models import RunInput
40
- from .serialization import Serializer
40
+ from .serialization import serialize_object
41
41
from .system import get_system
42
42
from .metadata import git_info
43
43
from .utilities import (
@@ -159,7 +159,7 @@ def __exit__(
159
159
else :
160
160
if self ._active :
161
161
self .log_event (f"{ exc_type .__name__ } : { value } " )
162
- if exc_type .__name__ in ("KeyboardInterrupt" ) and self ._active :
162
+ if exc_type .__name__ in ("KeyboardInterrupt" , ) and self ._active :
163
163
self .set_status ("terminated" )
164
164
else :
165
165
if traceback and self ._active :
@@ -982,17 +982,87 @@ def log_metrics(
982
982
@check_run_initialised
983
983
@skip_if_failed ("_aborted" , "_suppress_errors" , False )
984
984
@pydantic .validate_call
985
- def save (
985
+ def save_object (
986
986
self ,
987
- filename : str ,
987
+ obj : typing . Any ,
988
988
category : typing .Literal ["input" , "output" , "code" ],
989
- filetype : typing .Optional [str ] = None ,
990
- preserve_path : bool = False ,
991
989
name : typing .Optional [str ] = None ,
992
990
allow_pickle : bool = False ,
993
991
) -> bool :
992
+ """Save an object to the Simvue server
993
+
994
+ Parameters
995
+ ----------
996
+ obj : typing.Any
997
+ object to serialize and send to the server
998
+ category : Literal['input', 'output', 'code']
999
+ category of file with respect to this run
1000
+ name : str, optional
1001
+ name to associate with this object, by default None
1002
+ allow_pickle : bool, optional
1003
+ whether to allow pickling if all other serialization types fail, by default False
1004
+
1005
+ Returns
1006
+ -------
1007
+ bool
1008
+ whether object upload was successful
994
1009
"""
995
- Upload file or object
1010
+ serialized = serialize_object (obj , allow_pickle )
1011
+
1012
+ if not serialized or not (pickled := serialized [0 ]):
1013
+ self ._error (f"Failed to serialize '{ obj } '" )
1014
+ return False
1015
+
1016
+ data_type = serialized [1 ]
1017
+
1018
+ if not data_type and not allow_pickle :
1019
+ self ._error ("Unable to save Python object, set allow_pickle to True" )
1020
+ return False
1021
+
1022
+ data : dict [str , typing .Any ] = {
1023
+ "pickled" : pickled ,
1024
+ "type" : data_type ,
1025
+ "checksum" : calculate_sha256 (pickled , False ),
1026
+ "originalPath" : "" ,
1027
+ "size" : sys .getsizeof (pickled ),
1028
+ "name" : name ,
1029
+ "run" : self ._name ,
1030
+ "category" : category ,
1031
+ "storage" : self ._storage_id ,
1032
+ }
1033
+
1034
+ # Register file
1035
+ return self ._simvue is not None and self ._simvue .save_file (data ) is not None
1036
+
1037
+ @skip_if_failed ("_aborted" , "_suppress_errors" , False )
1038
+ @pydantic .validate_call
1039
+ def save_file (
1040
+ self ,
1041
+ file_path : pydantic .FilePath ,
1042
+ category : typing .Literal ["input" , "output" , "code" ],
1043
+ filetype : typing .Optional [str ] = None ,
1044
+ preserve_path : bool = False ,
1045
+ name : typing .Optional [str ] = None ,
1046
+ ) -> bool :
1047
+ """Upload file to the server
1048
+
1049
+ Parameters
1050
+ ----------
1051
+ file_path : pydantic.FilePath
1052
+ path to the file to upload
1053
+ category : Literal['input', 'output', 'code']
1054
+ category of file with respect to this run
1055
+ filetype : str, optional
1056
+ the MIME file type else this is deduced, by default None
1057
+ preserve_path : bool, optional
1058
+ whether to preserve the path during storage, by default False
1059
+ name : str, optional
1060
+ name to associate with this file, by default None
1061
+
1062
+ Returns
1063
+ -------
1064
+ bool
1065
+ whether the upload was successful
996
1066
"""
997
1067
if self ._mode == "disabled" :
998
1068
return True
@@ -1005,96 +1075,48 @@ def save(
1005
1075
self ._error ("Cannot upload output files for runs in the created state" )
1006
1076
return False
1007
1077
1008
- is_file : bool = False
1078
+ mimetypes .init ()
1079
+ mimetypes_valid = ["application/vnd.plotly.v1+json" ]
1080
+ mimetypes_valid += list (mimetypes .types_map .values ())
1009
1081
1010
- if isinstance (filename , str ):
1011
- if not os .path .isfile (filename ):
1012
- self ._error (f"File { filename } does not exist" )
1013
- return False
1014
- else :
1015
- is_file = True
1016
-
1017
- if filetype :
1018
- mimetypes_valid = ["application/vnd.plotly.v1+json" ]
1019
- mimetypes .init ()
1020
- for _ , value in mimetypes .types_map .items ():
1021
- mimetypes_valid .append (value )
1022
-
1023
- if filetype not in mimetypes_valid :
1024
- self ._error ("Invalid MIME type specified" )
1025
- return False
1026
-
1027
- data : dict [str , typing .Any ] = {}
1028
-
1029
- if preserve_path :
1030
- data ["name" ] = filename
1031
- if data ["name" ].startswith ("./" ):
1032
- data ["name" ] = data ["name" ][2 :]
1033
- elif is_file :
1034
- data ["name" ] = os .path .basename (filename )
1035
-
1036
- if name :
1037
- data ["name" ] = name
1038
-
1039
- data ["run" ] = self ._name
1040
- data ["category" ] = category
1082
+ if filetype and filetype not in mimetypes_valid :
1083
+ self ._error (f"Invalid MIME type '{ filetype } ' specified" )
1084
+ return False
1041
1085
1042
- if is_file :
1043
- data ["size" ] = os .path .getsize (filename )
1044
- data ["originalPath" ] = os .path .abspath (
1045
- os .path .expanduser (os .path .expandvars (filename ))
1046
- )
1047
- data ["checksum" ] = calculate_sha256 (filename , is_file )
1086
+ stored_file_name : str = f"{ file_path } "
1048
1087
1049
- if data ["size" ] == 0 :
1050
- click .secho (
1051
- "WARNING: saving zero-sized files not currently supported" ,
1052
- bold = True ,
1053
- fg = "yellow" ,
1054
- )
1055
- return True
1088
+ if preserve_path and stored_file_name .startswith ("./" ):
1089
+ stored_file_name = stored_file_name [2 :]
1090
+ elif not preserve_path :
1091
+ stored_file_name = os .path .basename (file_path )
1056
1092
1057
1093
# Determine mimetype
1058
- mimetype = None
1059
- if not filetype and is_file :
1060
- mimetypes .init ()
1061
- mimetype = mimetypes .guess_type (filename )[0 ]
1062
- if not mimetype :
1063
- mimetype = "application/octet-stream"
1064
- elif is_file :
1065
- mimetype = filetype
1066
-
1067
- if mimetype :
1068
- data ["type" ] = mimetype
1069
-
1070
- if not is_file :
1071
- serialized = Serializer ().serialize (filename , allow_pickle )
1072
-
1073
- if not serialized or not (pickled := serialized [0 ]):
1074
- self ._error (f"Failed to serialize '{ filename } '" )
1075
- return False
1076
-
1077
- data_type = serialized [1 ]
1078
-
1079
- data ["pickled" ] = pickled
1080
- data ["type" ] = data_type
1081
-
1082
- if not data ["type" ] and not allow_pickle :
1083
- self ._error ("Unable to save Python object, set allow_pickle to True" )
1084
- return False
1094
+ if not (mimetype := filetype ):
1095
+ mimetype = mimetypes .guess_type (file_path )[0 ] or "application/octet-stream"
1085
1096
1086
- data ["checksum" ] = calculate_sha256 (pickled , False )
1087
- data ["originalPath" ] = ""
1088
- data ["size" ] = sys .getsizeof (pickled )
1097
+ data : dict [str , typing .Any ] = {
1098
+ "name" : name or stored_file_name ,
1099
+ "run" : self ._name ,
1100
+ "type" : mimetype ,
1101
+ "storage" : self ._storage_id ,
1102
+ "category" : category ,
1103
+ "size" : (file_size := os .path .getsize (file_path )),
1104
+ "originalPath" : os .path .abspath (
1105
+ os .path .expanduser (os .path .expandvars (file_path ))
1106
+ ),
1107
+ "checksum" : calculate_sha256 (f"{ file_path } " , True ),
1108
+ }
1089
1109
1090
- if self ._storage_id :
1091
- data ["storage" ] = self ._storage_id
1110
+ if not file_size :
1111
+ click .secho (
1112
+ "WARNING: saving zero-sized files not currently supported" ,
1113
+ bold = True ,
1114
+ fg = "yellow" ,
1115
+ )
1116
+ return True
1092
1117
1093
1118
# Register file
1094
- if not self ._simvue .save_file (data ):
1095
- return False
1096
-
1097
- return True
1119
+ return self ._simvue .save_file (data ) is not None
1098
1120
1099
1121
@check_run_initialised
1100
1122
@skip_if_failed ("_aborted" , "_suppress_errors" , False )
@@ -1129,7 +1151,7 @@ def save_directory(
1129
1151
for dirpath , _ , filenames in directory .walk ():
1130
1152
for filename in filenames :
1131
1153
if (full_path := dirpath .joinpath (filename )).is_file ():
1132
- self .save ( f" { full_path } " , category , filetype , preserve_path )
1154
+ self .save_file ( full_path , category , filetype , preserve_path )
1133
1155
1134
1156
return True
1135
1157
@@ -1153,7 +1175,7 @@ def save_all(
1153
1175
1154
1176
for item in items :
1155
1177
if item .is_file ():
1156
- save_file = self .save ( f" { item } " , category , filetype , preserve_path )
1178
+ save_file = self .save_file ( item , category , filetype , preserve_path )
1157
1179
elif item .is_dir ():
1158
1180
save_file = self .save_directory (item , category , filetype , preserve_path )
1159
1181
else :
0 commit comments