@@ -136,6 +136,49 @@ def __init__(
136
136
def __enter__ (self ) -> "Run" :
137
137
return self
138
138
139
+ def _handle_exception_throw (
140
+ self ,
141
+ exc_type : typing .Optional [typing .Type [BaseException ]],
142
+ value : BaseException ,
143
+ traceback : typing .Optional [
144
+ typing .Union [typing .Type [BaseException ], BaseException ]
145
+ ],
146
+ ) -> None :
147
+ _exception_thrown : typing .Optional [str ] = (
148
+ exc_type .__name__ if exc_type else None
149
+ )
150
+ _is_running : bool = self ._status == "running"
151
+ _is_running_online : bool = self ._id is not None and _is_running
152
+ _is_running_offline : bool = self ._mode == "offline" and _is_running
153
+ _is_terminated : bool = (
154
+ _exception_thrown is not None and _exception_thrown == "KeyboardInterrupt"
155
+ )
156
+
157
+ if not _exception_thrown and _is_running :
158
+ return
159
+
160
+ # Abort executor processes
161
+ self ._executor .kill_all ()
162
+
163
+ if not _is_running :
164
+ return
165
+
166
+ self .set_status ("terminated" if _is_terminated else "failed" )
167
+
168
+ if not self ._active :
169
+ return
170
+
171
+ # If the dispatcher has already been aborted then this will
172
+ # fail so just continue without the event
173
+ with contextlib .suppress (RuntimeError ):
174
+ self .log_event (f"{ _exception_thrown } : { value } " )
175
+
176
+ if not traceback :
177
+ return
178
+
179
+ with contextlib .suppress (RuntimeError ):
180
+ self .log_event (f"Traceback: { traceback } " )
181
+
139
182
def __exit__ (
140
183
self ,
141
184
exc_type : typing .Optional [typing .Type [BaseException ]],
@@ -144,64 +187,16 @@ def __exit__(
144
187
typing .Union [typing .Type [BaseException ], BaseException ]
145
188
],
146
189
) -> None :
147
- identifier = self ._id
148
190
logger .debug (
149
191
"Automatically closing run '%s' in status %s" ,
150
- identifier if self ._mode == "online" else "unregistered" ,
192
+ self . _id if self ._mode == "online" else "unregistered" ,
151
193
self ._status ,
152
194
)
153
195
154
- self ._executor .wait_for_completion ()
196
+ # Exception handling
197
+ self ._handle_exception_throw (exc_type , value , traceback )
155
198
156
- # Stop the run heartbeat
157
- if self ._heartbeat_thread and self ._heartbeat_termination_trigger :
158
- self ._heartbeat_termination_trigger .set ()
159
- self ._heartbeat_thread .join ()
160
-
161
- # Handle case where run is aborted by user KeyboardInterrupt
162
- if (self ._id or self ._mode == "offline" ) and self ._status == "running" :
163
- if not exc_type :
164
- if self ._shutdown_event is not None :
165
- self ._shutdown_event .set ()
166
- if self ._dispatcher :
167
- self ._dispatcher .join ()
168
- self .set_status ("completed" )
169
- else :
170
- if self ._active :
171
- # If the dispatcher has already been aborted then this will
172
- # fail so just continue without the event
173
- with contextlib .suppress (RuntimeError ):
174
- self .log_event (f"{ exc_type .__name__ } : { value } " )
175
- if exc_type .__name__ in ("KeyboardInterrupt" ,) and self ._active :
176
- self .set_status ("terminated" )
177
- else :
178
- if traceback and self ._active :
179
- with contextlib .suppress (RuntimeError ):
180
- self .log_event (f"Traceback: { traceback } " )
181
- self .set_status ("failed" )
182
- else :
183
- if self ._shutdown_event is not None :
184
- self ._shutdown_event .set ()
185
- if self ._dispatcher :
186
- self ._dispatcher .purge ()
187
- self ._dispatcher .join ()
188
-
189
- if _non_zero := self .executor .exit_status :
190
- _error_msgs : dict [str , typing .Optional [str ]] = (
191
- self .executor .get_error_summary ()
192
- )
193
- _error_msg = "\n " .join (
194
- f"{ identifier } :\n { msg } " for identifier , msg in _error_msgs .items ()
195
- )
196
- if _error_msg :
197
- _error_msg = f":\n { _error_msg } "
198
- click .secho (
199
- "[simvue] Process executor terminated with non-zero exit status "
200
- f"{ _non_zero } { _error_msg } " ,
201
- fg = "red" if self ._term_color else None ,
202
- bold = self ._term_color ,
203
- )
204
- sys .exit (_non_zero )
199
+ self ._tidy_run ()
205
200
206
201
@property
207
202
def duration (self ) -> float :
@@ -1431,26 +1426,8 @@ def set_status(
1431
1426
1432
1427
return False
1433
1428
1434
- @skip_if_failed ("_aborted" , "_suppress_errors" , False )
1435
- def close (self ) -> bool :
1436
- """Close the run
1437
-
1438
- Returns
1439
- -------
1440
- bool
1441
- whether close was successful
1442
- """
1429
+ def _tidy_run (self ) -> None :
1443
1430
self ._executor .wait_for_completion ()
1444
- if self ._mode == "disabled" :
1445
- return True
1446
-
1447
- if not self ._simvue :
1448
- self ._error ("Cannot close run, not initialised" )
1449
- return False
1450
-
1451
- if not self ._active :
1452
- self ._error ("Run is not active" )
1453
- return False
1454
1431
1455
1432
if self ._heartbeat_thread and self ._heartbeat_termination_trigger :
1456
1433
self ._heartbeat_termination_trigger .set ()
@@ -1459,7 +1436,7 @@ def close(self) -> bool:
1459
1436
if self ._shutdown_event :
1460
1437
self ._shutdown_event .set ()
1461
1438
1462
- if self ._status != "failed " :
1439
+ if self ._status == "running " :
1463
1440
if self ._dispatcher :
1464
1441
self ._dispatcher .join ()
1465
1442
self .set_status ("completed" )
@@ -1484,6 +1461,29 @@ def close(self) -> bool:
1484
1461
)
1485
1462
sys .exit (_non_zero )
1486
1463
1464
+ @skip_if_failed ("_aborted" , "_suppress_errors" , False )
1465
+ def close (self ) -> bool :
1466
+ """Close the run
1467
+
1468
+ Returns
1469
+ -------
1470
+ bool
1471
+ whether close was successful
1472
+ """
1473
+ self ._executor .wait_for_completion ()
1474
+ if self ._mode == "disabled" :
1475
+ return True
1476
+
1477
+ if not self ._simvue :
1478
+ self ._error ("Cannot close run, not initialised" )
1479
+ return False
1480
+
1481
+ if not self ._active :
1482
+ self ._error ("Run is not active" )
1483
+ return False
1484
+
1485
+ self ._tidy_run ()
1486
+
1487
1487
return True
1488
1488
1489
1489
@skip_if_failed ("_aborted" , "_suppress_errors" , False )
0 commit comments