Skip to content

Commit 99b3e96

Browse files
committed
Copied current robotremoteserver.py from Robot Framework.
Contains fixes in RF 2.8.3 and 2.8.4: - **kwarts support: http://code.google.com/p/robotframework/issues/detail?id=1596 - support for Mapping: http://code.google.com/p/robotframework/issues/detail?id=1597 - support for binary data: http://code.google.com/p/robotframework/issues/detail?id=1606 - use 127.0.0.1 instead of localhost: http://code.google.com/p/robotframework/issues/detail?id=1607 Also erases some enhancements done in this project earlier. Will add them back next.
1 parent 16a96f3 commit 99b3e96

1 file changed

Lines changed: 49 additions & 28 deletions

File tree

src/robotremoteserver.py

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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.
@@ -12,32 +12,38 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import re
1516
import sys
1617
import inspect
1718
import traceback
1819
from StringIO import StringIO
1920
from SimpleXMLRPCServer import SimpleXMLRPCServer
21+
from xmlrpclib import Binary
2022
try:
2123
import signal
2224
except 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

2935
class 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

Comments
 (0)