Skip to content

Commit cef0506

Browse files
added Handler wrapper for better argument resolving when calling Keywords with TestRobot
--HG-- branch : dev
1 parent 9c162f9 commit cef0506

3 files changed

Lines changed: 104 additions & 7 deletions

File tree

robottools/testrobot/context.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from robot.running.namespace import Importer
3030

3131
from .keyword import Keyword
32+
from .handler import Handler
3233

3334

3435
class Context(object):
@@ -87,7 +88,11 @@ def get_handler(self, name):
8788

8889
def get_runner(self, name):
8990
handler = self.get_handler(name)
90-
return handler.create_runner(name)
91+
runner = handler.create_runner(name)
92+
# wrap the handler for Pythonic args/kwargs handling
93+
# as needed by .keyword.Keyword.__call__
94+
runner._handler = Handler(runner._handler)
95+
return runner
9196

9297
def start_keyword(self, keyword):
9398
pass

robottools/testrobot/handler.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# robotframework-tools
2+
#
3+
# Python Tools for Robot Framework and Test Libraries.
4+
#
5+
# Copyright (C) 2013-2016 Stefan Zimmermann <zimmermann.code@gmail.com>
6+
#
7+
# robotframework-tools is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# robotframework-tools is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with robotframework-tools. If not, see <http://www.gnu.org/licenses/>.
19+
20+
"""robottools.testrobot.handler
21+
22+
Making ``robot.running.handlers`` work better with
23+
:class:`robottools.TestRobot`.
24+
25+
.. moduleauthor:: Stefan Zimmermann <zimmermann.code@gmail.com>
26+
"""
27+
__all__ = ['Handler']
28+
29+
from moretools import isstring, dictitems
30+
31+
32+
class Handler(object):
33+
"""A wrapper for instances of classes from ``robot.running.handlers``,
34+
implementing a custom :meth:`.resolve_arguments`,
35+
which supports passing arbitrary python objects
36+
as keyword argument values.
37+
"""
38+
def __init__(self, handler):
39+
"""Create with the `handler` instance to wrap.
40+
"""
41+
self._handler = handler
42+
43+
def __getattr__(self, name):
44+
return getattr(self._handler, name)
45+
46+
# HACK
47+
def resolve_arguments(self, args_and_kwargs, variables):
48+
"""More Pythonic argument handling for interactive
49+
:class:`robottools.testrobot.keyword.Keyword` calls.
50+
51+
Original ``resolve_arguments`` methods from ``robot.running.handlers``
52+
expect as first argument a single list of Keyword arguments
53+
coming from an RFW script::
54+
55+
['arg0', 'arg1', ..., 'name=value', ...]
56+
57+
So there is no chance to pass unstringified named argument values.
58+
Only unstringified positional arguments are possible.
59+
60+
This wrapper method takes a normal Python `args_and_kwargs` pair
61+
instead as first argument::
62+
63+
(arg0, arg1, ...), {name: value, ...}
64+
65+
It resolves the named arguments stringified via the original method
66+
but returns the original Python values::
67+
68+
[arg0, arg1, ...], [(name, value), ...]
69+
70+
Only strings are untouched.
71+
So RFW ``'...${variable}...'`` substitution still works.
72+
"""
73+
posargs, kwargs = args_and_kwargs
74+
rfwargs = list(posargs)
75+
# prepare 'key=value' strings for original RFW method
76+
for name, value in dictitems(kwargs):
77+
if not isstring(value):
78+
value = repr(value)
79+
rfwargs.append(u'%s=%s' % (name, value))
80+
posargs, rfwkwargslist \
81+
= self._handler.resolve_arguments(rfwargs, variables)
82+
# and replace values with original non-string objects after resolving
83+
kwargslist = []
84+
for name, rfwvalue in rfwkwargslist:
85+
value = kwargs[name]
86+
if isstring(value):
87+
value = rfwvalue
88+
kwargslist.append(value)
89+
return posargs, kwargslist

robottools/testrobot/keyword.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121
2222
.. moduleauthor:: Stefan Zimmermann <zimmermann.code@gmail.com>
2323
"""
24-
from six import reraise, text_type as unicode
25-
2624
__all__ = ['Keyword']
2725

2826
import sys
27+
from six import reraise, text_type as unicode
2928

3029
from robot.errors import HandlerExecutionFailed, ExecutionFailed
3130
import robot.running
@@ -134,12 +133,16 @@ def __doc__(self):
134133
return KeywordInspector.__doc__.fget(self)
135134

136135
def __call__(self, *args, **kwargs):
137-
args = list(map(unicode, args))
138-
args.extend(u'%s=%s' % item for item in kwargs.items())
136+
# HACK: normally, RFW's Keyword argument resolvers expect
137+
# a plain list of arguments coming from an RFW script,
138+
# which has limitations, as described in the .handler.Handler wrapper,
139+
# which is patched later into the actual Keyword function runner
140+
# by self._context.get_runner(),
141+
# and which expects the (args, kwargs) pair instead
139142
if self._debug:
140-
runner = DebugKeyword(self.name, args=args)
143+
runner = DebugKeyword(self.name, args=(args, kwargs))
141144
else:
142-
runner = robot.running.Keyword(self.name, args=args)
145+
runner = robot.running.Keyword(self.name, args=(args, kwargs))
143146
# HACK: `with` registers Context to EXECUTION_CONTEXTS
144147
# and Output to LOGGER:
145148
with self._context as ctx:

0 commit comments

Comments
 (0)