Skip to content

Commit 82e348e

Browse files
committed
Split keyword result handling into separate class
1 parent 98a3a03 commit 82e348e

File tree

1 file changed

+57
-50
lines changed

1 file changed

+57
-50
lines changed

src/robotremoteserver.py

Lines changed: 57 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -200,59 +200,76 @@ def get_keyword_documentation(self, name):
200200

201201

202202
class KeywordRunner(object):
203-
_generic_exceptions = (AssertionError, RuntimeError, Exception)
204-
_fatal_exceptions = (SystemExit, KeyboardInterrupt)
205203

206204
def __init__(self, keyword):
207205
self._keyword = keyword
208206

209207
def run_keyword(self, args, kwargs=None):
210208
args, kwargs = self._handle_binary_args(args, kwargs or {})
211-
result = {'status': 'FAIL'}
209+
result = KeywordResult()
212210
self._intercept_std_streams()
213211
try:
214212
return_value = self._keyword(*args, **kwargs)
215-
except:
216-
exc_type, exc_value, exc_tb = sys.exc_info()
217-
if exc_type in self._fatal_exceptions:
218-
self._restore_std_streams()
219-
raise
220-
self._add_to_result(result, 'error',
221-
self._get_error_message(exc_type, exc_value))
222-
self._add_to_result(result, 'traceback',
223-
self._get_error_traceback(exc_tb))
224-
self._add_to_result(result, 'continuable',
225-
self._get_error_attribute(exc_value, 'CONTINUE'),
226-
default=False)
227-
self._add_to_result(result, 'fatal',
228-
self._get_error_attribute(exc_value, 'EXIT'),
229-
default=False)
213+
except Exception:
214+
result.set_error(*sys.exc_info())
230215
else:
231216
try:
232-
self._add_to_result(result, 'return',
233-
self._handle_return_value(return_value))
234-
except:
235-
exc_type, exc_value, _ = sys.exc_info()
236-
self._add_to_result(result, 'error',
237-
self._get_error_message(exc_type, exc_value))
217+
result.set_return(return_value)
218+
except Exception:
219+
result.set_error(*sys.exc_info()[:2])
238220
else:
239-
result['status'] = 'PASS'
240-
self._add_to_result(result, 'output', self._restore_std_streams())
241-
return result
221+
result.set_status('PASS')
222+
finally:
223+
result.set_output(self._restore_std_streams())
224+
return result.data
242225

243226
def _handle_binary_args(self, args, kwargs):
244227
args = [self._handle_binary_arg(a) for a in args]
245228
kwargs = dict((k, self._handle_binary_arg(v)) for k, v in kwargs.items())
246229
return args, kwargs
247230

248231
def _handle_binary_arg(self, arg):
249-
if isinstance(arg, Binary):
250-
return arg.data
251-
return arg
232+
return arg if not isinstance(arg, Binary) else arg.data
233+
234+
def _intercept_std_streams(self):
235+
sys.stdout = StringIO()
236+
sys.stderr = StringIO()
237+
238+
def _restore_std_streams(self):
239+
stdout = sys.stdout.getvalue()
240+
stderr = sys.stderr.getvalue()
241+
close = [sys.stdout, sys.stderr]
242+
sys.stdout = sys.__stdout__
243+
sys.stderr = sys.__stderr__
244+
for stream in close:
245+
stream.close()
246+
if stdout and stderr:
247+
if not stderr.startswith(('*TRACE*', '*DEBUG*', '*INFO*', '*HTML*',
248+
'*WARN*', '*ERROR*')):
249+
stderr = '*INFO* %s' % stderr
250+
if not stdout.endswith('\n'):
251+
stdout += '\n'
252+
return stdout + stderr
253+
252254

253-
def _add_to_result(self, result, key, value, default=''):
255+
class KeywordResult(object):
256+
_generic_exceptions = (AssertionError, RuntimeError, Exception)
257+
258+
def __init__(self):
259+
self.data = {'status': 'FAIL'}
260+
261+
def set_error(self, exc_type, exc_value, exc_tb=None):
262+
self._add('error', self._get_error_message(exc_type, exc_value))
263+
if exc_tb:
264+
self._add('traceback', self._get_error_traceback(exc_tb))
265+
self._add('continuable', self._get_error_attribute(exc_value, 'CONTINUE'),
266+
default=False)
267+
self._add('fatal', self._get_error_attribute(exc_value, 'EXIT'),
268+
default=False)
269+
270+
def _add(self, key, value, default=''):
254271
if value != default:
255-
result[key] = value
272+
self.data[key] = value
256273

257274
def _get_error_message(self, exc_type, exc_value):
258275
name = exc_type.__name__
@@ -282,6 +299,9 @@ def _get_error_traceback(self, exc_tb):
282299
def _get_error_attribute(self, exc_value, name):
283300
return bool(getattr(exc_value, 'ROBOT_%s_ON_FAILURE' % name, False))
284301

302+
def set_return(self, value):
303+
self._add('return', self._handle_return_value(value))
304+
285305
def _handle_return_value(self, ret):
286306
if isinstance(ret, (str, unicode, bytes)):
287307
return self._handle_binary_result(ret)
@@ -323,25 +343,12 @@ def _str(self, item, handle_binary=True):
323343
item = self._handle_binary_result(item)
324344
return item
325345

326-
def _intercept_std_streams(self):
327-
sys.stdout = StringIO()
328-
sys.stderr = StringIO()
346+
def set_status(self, status):
347+
self.data['status'] = status
329348

330-
def _restore_std_streams(self):
331-
stdout = sys.stdout.getvalue()
332-
stderr = sys.stderr.getvalue()
333-
close = [sys.stdout, sys.stderr]
334-
sys.stdout = sys.__stdout__
335-
sys.stderr = sys.__stderr__
336-
for stream in close:
337-
stream.close()
338-
if stdout and stderr:
339-
if not stderr.startswith(('*TRACE*', '*DEBUG*', '*INFO*', '*HTML*',
340-
'*WARN*', '*ERROR*')):
341-
stderr = '*INFO* %s' % stderr
342-
if not stdout.endswith('\n'):
343-
stdout += '\n'
344-
return self._handle_binary_result(stdout + stderr)
349+
def set_output(self, output):
350+
if output:
351+
self.data['output'] = self._handle_binary_result(output)
345352

346353

347354
if __name__ == '__main__':

0 commit comments

Comments
 (0)