1- # Copyright 2008-2012 Nokia Siemens Networks Oyj
1+ # Copyright 2008-2013 Nokia Siemens Networks Oyj
22#
33# Licensed under the Apache License, Version 2.0 (the "License");
44# you may not use this file except in compliance with the License.
1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ import re
1516import sys
1617import inspect
1718import traceback
1819from StringIO import StringIO
1920from SimpleXMLRPCServer import SimpleXMLRPCServer
21+ from xmlrpclib import Binary
2022try :
2123 import signal
2224except ImportError :
2325 signal = None
26+ try :
27+ from collections import Mapping
28+ except ImportError :
29+ Mapping = dict
2430
2531
26- __version__ = 'devel'
32+ BINARY = re . compile ( '[ \x00 - \x08 \x0B \x0C \x0E - \x1F ]' )
2733
2834
2935class RobotRemoteServer (SimpleXMLRPCServer ):
3036 allow_reuse_address = True
31- _generic_exceptions = (AssertionError , RuntimeError , Exception )
32- _fatal_exceptions = (SystemExit , KeyboardInterrupt )
3337
34- def __init__ (self , library , host = 'localhost ' , port = 8270 , allow_stop = True ):
38+ def __init__ (self , library , host = '127.0.0.1 ' , port = 8270 , allow_stop = True ):
3539 SimpleXMLRPCServer .__init__ (self , (host , int (port )), logRequests = False )
3640 self ._library = library
3741 self ._allow_stop = allow_stop
42+ self ._shutdown = False
3843 self ._register_functions ()
3944 self ._register_signal_handlers ()
40- self ._log ('Robot Framework remote server starting at %s:%s' % (host , port ))
45+ self ._log ('Robot Framework remote server starting at %s:%s'
46+ % (host , port ))
4147 self .serve_forever ()
4248
4349 def _register_functions (self ):
@@ -57,7 +63,6 @@ def stop_with_signal(signum, frame):
5763 signal .signal (signal .SIGINT , stop_with_signal )
5864
5965 def serve_forever (self ):
60- self ._shutdown = False
6166 while not self ._shutdown :
6267 self .handle_request ()
6368
@@ -80,35 +85,46 @@ def get_keyword_names(self):
8085 and inspect .isroutine (getattr (self ._library , attr ))]
8186 return names + ['stop_remote_server' ]
8287
83- def run_keyword (self , name , args ):
84- result = {'error' : '' , 'traceback' : '' , 'return' : '' }
88+ def run_keyword (self , name , args , kwargs = None ):
89+ args , kwargs = self ._handle_binary_args (args , kwargs or {})
90+ result = {'status' : 'PASS' , 'return' : '' , 'output' : '' ,
91+ 'error' : '' , 'traceback' : '' }
8592 self ._intercept_stdout ()
8693 try :
87- return_value = self ._get_keyword (name )(* args )
94+ return_value = self ._get_keyword (name )(* args , ** kwargs )
8895 except :
8996 result ['status' ] = 'FAIL'
9097 result ['error' ], result ['traceback' ] = self ._get_error_details ()
9198 else :
92- result ['status' ] = 'PASS'
9399 result ['return' ] = self ._handle_return_value (return_value )
94100 result ['output' ] = self ._restore_stdout ()
95101 return result
96102
103+ def _handle_binary_args (self , args , kwargs ):
104+ args = [self ._handle_binary_arg (a ) for a in args ]
105+ kwargs = dict ([(k , self ._handle_binary_arg (v )) for k , v in kwargs .items ()])
106+ return args , kwargs
107+
108+ def _handle_binary_arg (self , arg ):
109+ return arg if not isinstance (arg , Binary ) else str (arg )
110+
97111 def get_keyword_arguments (self , name ):
98112 kw = self ._get_keyword (name )
99113 if not kw :
100114 return []
101115 return self ._arguments_from_kw (kw )
102116
103117 def _arguments_from_kw (self , kw ):
104- args , varargs , _ , defaults = inspect .getargspec (kw )
118+ args , varargs , kwargs , defaults = inspect .getargspec (kw )
105119 if inspect .ismethod (kw ):
106120 args = args [1 :] # drop 'self'
107121 if defaults :
108122 args , names = args [:- len (defaults )], args [- len (defaults ):]
109123 args += ['%s=%s' % (n , d ) for n , d in zip (names , defaults )]
110124 if varargs :
111125 args .append ('*%s' % varargs )
126+ if kwargs :
127+ args .append ('**%s' % kwargs )
112128 return args
113129
114130 def get_keyword_documentation (self , name ):
@@ -128,43 +144,48 @@ def _get_keyword(self, name):
128144
129145 def _get_error_details (self ):
130146 exc_type , exc_value , exc_tb = sys .exc_info ()
131- if exc_type in self . _fatal_exceptions :
147+ if exc_type in ( SystemExit , KeyboardInterrupt ) :
132148 self ._restore_stdout ()
133149 raise
134150 return (self ._get_error_message (exc_type , exc_value ),
135151 self ._get_error_traceback (exc_tb ))
136152
137153 def _get_error_message (self , exc_type , exc_value ):
138154 name = exc_type .__name__
139- message = self . _get_message_from_exception (exc_value )
155+ message = str (exc_value )
140156 if not message :
141157 return name
142- if exc_type in self . _generic_exceptions :
158+ if name in ( 'AssertionError' , 'RuntimeError' , 'Exception' ) :
143159 return message
144160 return '%s: %s' % (name , message )
145161
146- def _get_message_from_exception (self , value ):
147- # UnicodeError occurs below 2.6 and if message contains non-ASCII bytes
148- try :
149- return unicode (value )
150- except UnicodeError :
151- return ' ' .join ([unicode (a , errors = 'replace' ) for a in value .args ])
152-
153162 def _get_error_traceback (self , exc_tb ):
154163 # Latest entry originates from this class so it can be removed
155164 entries = traceback .extract_tb (exc_tb )[1 :]
156165 trace = '' .join (traceback .format_list (entries ))
157166 return 'Traceback (most recent call last):\n ' + trace
158167
159168 def _handle_return_value (self , ret ):
160- if isinstance (ret , (basestring , int , long , float )):
169+ if isinstance (ret , basestring ):
170+ return self ._handle_binary_result (ret )
171+ if isinstance (ret , (int , long , float )):
161172 return ret
162- if isinstance (ret , (tuple , list )):
163- return [self ._handle_return_value (item ) for item in ret ]
164- if isinstance (ret , dict ):
173+ if isinstance (ret , Mapping ):
165174 return dict ([(self ._str (key ), self ._handle_return_value (value ))
166175 for key , value in ret .items ()])
167- return self ._str (ret )
176+ try :
177+ return [self ._handle_return_value (item ) for item in ret ]
178+ except TypeError :
179+ return self ._str (ret )
180+
181+ def _handle_binary_result (self , result ):
182+ if not BINARY .search (result ):
183+ return result
184+ try :
185+ result = str (result )
186+ except UnicodeError :
187+ raise ValueError ("Cannot represent %r as binary." % result )
188+ return Binary (result )
168189
169190 def _str (self , item ):
170191 if item is None :
@@ -179,7 +200,7 @@ def _restore_stdout(self):
179200 output = sys .stdout .getvalue ()
180201 sys .stdout .close ()
181202 sys .stdout = sys .__stdout__
182- return output
203+ return self . _handle_binary_result ( output )
183204
184205 def _log (self , msg , level = None ):
185206 if level :
0 commit comments