Skip to content

Commit facef6b

Browse files
authored
Fix encoding of exit status of the pynisher (#1001)
1 parent 9e04bd8 commit facef6b

File tree

1 file changed

+37
-16
lines changed

1 file changed

+37
-16
lines changed

autosklearn/evaluation/__init__.py

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# -*- encoding: utf-8 -*-
22
import functools
3+
import json
34
import math
45
import multiprocessing
56
from queue import Empty
67
import time
78
import traceback
8-
from typing import Tuple
9+
from typing import Dict, List, Optional, Tuple, Union
910

11+
from ConfigSpace import Configuration
1012
import numpy as np
1113
import pynisher
1214
from smac.runhistory.runhistory import RunInfo, RunValue
@@ -81,6 +83,14 @@ def get_cost_of_crash(metric):
8183
return worst_possible_result
8284

8385

86+
def _encode_exit_status(exit_status):
87+
try:
88+
json.dumps(exit_status)
89+
return exit_status
90+
except (TypeError, OverflowError):
91+
return str(exit_status)
92+
93+
8494
# TODO potentially log all inputs to this class to pickle them in order to do
8595
# easier debugging of potential crashes
8696
class ExecuteTaFuncWithQueue(AbstractTAFunc):
@@ -224,11 +234,15 @@ def run_wrapper(
224234

225235
return super().run_wrapper(run_info=run_info)
226236

227-
def run(self, config, instance=None,
228-
cutoff=None,
229-
seed=12345,
230-
budget=0.0,
231-
instance_specific=None):
237+
def run(
238+
self,
239+
config: Configuration,
240+
instance: Optional[str] = None,
241+
cutoff: Optional[float] = None,
242+
seed: int = 12345,
243+
budget: float = 0.0,
244+
instance_specific: Optional[str] = None,
245+
) -> Tuple[StatusType, float, float, Dict[str, Union[int, float, str, Dict, List, Tuple]]]:
232246

233247
queue = multiprocessing.Queue()
234248

@@ -272,11 +286,19 @@ def run(self, config, instance=None,
272286
obj_kwargs['resampling_strategy'] = self.resampling_strategy
273287
obj_kwargs['resampling_strategy_args'] = self.resampling_strategy_args
274288

275-
obj = pynisher.enforce_limits(**arguments)(self.ta)
276-
obj(**obj_kwargs)
277-
278-
if obj.exit_status in (pynisher.TimeoutException,
279-
pynisher.MemorylimitException):
289+
try:
290+
obj = pynisher.enforce_limits(**arguments)(self.ta)
291+
obj(**obj_kwargs)
292+
except Exception as e:
293+
exception_traceback = traceback.format_exc()
294+
error_message = repr(e)
295+
additional_info = {
296+
'traceback': exception_traceback,
297+
'error': error_message
298+
}
299+
return StatusType.CRASHED, self.cost_for_crash, 0.0, additional_info
300+
301+
if obj.exit_status in (pynisher.TimeoutException, pynisher.MemorylimitException):
280302
# Even if the pynisher thinks that a timeout or memout occured,
281303
# it can be that the target algorithm wrote something into the queue
282304
# - then we treat it as a succesful run
@@ -309,8 +331,7 @@ def run(self, config, instance=None,
309331
elif obj.exit_status is pynisher.MemorylimitException:
310332
status = StatusType.MEMOUT
311333
additional_run_info = {
312-
'error': 'Memout (used more than %d MB).' %
313-
self.memory_limit
334+
'error': 'Memout (used more than %d MB).' % self.memory_limit
314335
}
315336
else:
316337
raise ValueError(obj.exit_status)
@@ -322,7 +343,7 @@ def run(self, config, instance=None,
322343
cost = self.worst_possible_result
323344
additional_run_info = {'error': 'Your configuration of '
324345
'auto-sklearn does not work!',
325-
'exit_status': obj.exit_status,
346+
'exit_status': _encode_exit_status(obj.exit_status),
326347
'subprocess_stdout': obj.stdout,
327348
'subprocess_stderr': obj.stderr,
328349
}
@@ -343,14 +364,14 @@ def run(self, config, instance=None,
343364
'because the pynisher exit ' \
344365
'status %s is unknown.' % \
345366
str(obj.exit_status)
346-
additional_run_info['exit_status'] = obj.exit_status
367+
additional_run_info['exit_status'] = _encode_exit_status(obj.exit_status)
347368
additional_run_info['subprocess_stdout'] = obj.stdout
348369
additional_run_info['subprocess_stderr'] = obj.stderr
349370
except Empty:
350371
info = None
351372
additional_run_info = {
352373
'error': 'Result queue is empty',
353-
'exit_status': obj.exit_status,
374+
'exit_status': _encode_exit_status(obj.exit_status),
354375
'subprocess_stdout': obj.stdout,
355376
'subprocess_stderr': obj.stderr,
356377
'exitcode': obj.exitcode

0 commit comments

Comments
 (0)