@@ -200,59 +200,76 @@ def get_keyword_documentation(self, name):
200200
201201
202202class 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
347354if __name__ == '__main__' :
0 commit comments