How to pass named arguments list into the library using **kwargs

5,522 views
Skip to first unread message

Andrey Hitrin

unread,
Jun 13, 2013, 4:49:13 AM6/13/13
to robotframe...@googlegroups.com
Hello!

I'm trying to make my tests easier to read and maintain with new features implemented in RF 2.8. For example, I have custom Python library with function that capture arguments using **kwargs syntax (because I have a lot of optional arguments, and usually use only some of them in each case). This feature works fine when I call library method directly. But I want something more complicated. I want to create my own RF keyword that accepts named variable list, and then pass them into the library function. The reason behind this is that such "wrapper" keyword may calculate and add some obligatory arguments into that list before calling library function.

The problem is that such trick doesn't work simply.

Suppose we have an example library:

    def args_list(**kwargs):
        for name, value in kwargs.items():
            print name, value

I've tried several ways to call it from keyword, but all of them fails:

    *** Test Cases ***
    Simple Usage
        [Documentation]    passed
        Args List    hello=world

    Pass Into Wrapper, Variable Syntax
        [Documentation]    failed: Keyword 'lib.Args List' expected 0 non-keyword arguments, got 1
        Wrap1    hello=world

    Pass Into Wrapper, List Syntax
        [Documentation]    failed: Keyword 'lib.Args List' expected 0 non-keyword arguments, got 1
        Wrap2    hello=world

    Pass Into Wrapper, Escape Variable Syntax
        [Documentation]    failed: Keyword 'lib.Args List' expected 0 non-keyword arguments, got 1
        Wrap3    hello=world

    Pass Into Wrapper, Escape List Syntax
        [Documentation]    failed: Keyword 'lib.Args List' expected 0 non-keyword arguments, got 1
        Wrap4    hello=world

    *** Keywords ***
    Wrap1
        [Arguments]    @{args}
        Args List    ${args}

    Wrap2
        [Arguments]    @{args}
        Args List    @{args}

    Wrap3
        [Arguments]    @{args}
        Args List    \${args}

    Wrap4
        [Arguments]    @{args}
        Args List    \@{args}

As I consider, the problem is that when I use Wrap keywords, my list is no more treated as a list of keyword arguments, but instead becomes just a list of strings: [u'hello=world']. So I can make things work using following hackery:

    def args_list1(*args):
        for name, value in list_to_pseudo_dict(args):
            print name, value

    def list_to_pseudo_dict(l):
        def extract_argument_name(x):
            argument_name = x.split('=')[0]
            argument_value = x[len(argument_name) + 1:]
            return [argument_name, argument_value]
        return list((x[0], x[1]) for x in map(extract_argument_name, l))

Maybe my Python is not very good, but I hope this code is understandable.

Only with this hack I can use custom wrapper:

    Transform List To Dict
        [Documentation]    passed
        Wrap5    hello=world

    Wrap5
        [Arguments]    @{args}
        Append To List1    ${args}    meaning_of_life=42
        Args List    @{args}

And each key in that list is being transformed into pseudo-dict key.

After all, my question is: is there a way to do something described here without given hacks? Or, maybe, will it be implemented in the following versions of Robot Framework?

Thank you.

Pekka Klärck

unread,
Jun 13, 2013, 8:14:40 AM6/13/13
to andrey...@gmail.com, robotframe...@googlegroups.com
2013/6/13 Andrey Hitrin <andrey...@gmail.com>:
>
> I'm trying to make my tests easier to read and maintain with new features
> implemented in RF 2.8. For example, I have custom Python library with
> function that capture arguments using **kwargs syntax (because I have a lot
> of optional arguments, and usually use only some of them in each case). This
> feature works fine when I call library method directly.

Great to hear people are already taking **kwargs into use!

> But I want something more complicated. I want to create my own RF keyword that accepts named
> variable list, and then pass them into the library function. The reason
> behind this is that such "wrapper" keyword may calculate and add some
> obligatory arguments into that list before calling library function.
>
> The problem is that such trick doesn't work simply.

Unfortunately that is not supported currently. Named arguments syntax
that also kwargs support uses is designed so that the name needs to be
visible in the keyword call. This is discussed a bit more thoroughly
in the User Guide:
http://robotframework.googlecode.com/hg/doc/userguide/RobotFrameworkUserGuide.html#named-arguments-with-variables

We were aware that this limitation makes the new kwargs syntax less
useful, but we decided it's better to first get kwargs into some use
and then see how to make them easier to use with user keywords. There
are, at least, the following two options we can consider:

1) Add some new syntax to declare that a user keyword accepts free
kwargs and make it possible to pass them further. For example
something like this:

*** Test Cases ***
Example
Keyword command arg1 arg2 shell=True cwd=${TEMPDIR}

*** Keywords ***
Keyword
[Arguments] ${command} @{args} &{config}
Run Process ${command} @{args} &{config}

In the above example, inside Keyword, variable ${command} could have
value 'command', @{args} would contain list ['arg1', 'arg2'], and new
"dict variable" &{config} would contain {'shell': 'True', 'cwd':
'/tmp'}.

Similarly like list variable @{list} can be used as scalar ${list},
and in RF 2.8 ${var} containing a list as @{var}, it should be
possible to use &{dict} as ${dict} and ${var} containing a dict like
&{var}:

${config} = Create Dictionary shell True
Run Process command &{config}

A problem wit this approach is that it requires quite a bit of work.


2) Add support to Python 3 style "keyword only arguments"
<http://www.python.org/dev/peps/pep-3102> to user keywords:

*** Keywords ***
Keyword
[Arguments] ${command} @{args} ${shell}=True ${cwd}=.
Run Process ${command} @{args} shell=${shell} cwd=${cwd}

This would likely be easier to implement but not as flexible as the
option 1). It would not, for example, solve the original problem
illustrated in this thread.


Neither of the above solutions shouldn't cause backwards compatibility
issues. They could thus potentially be implemented already in RF 2.8.x
minor releases. I want to see how people like the new **kwargs support
in general before that, though.

Cheers,
.pkee
--
Agile Tester/Developer/Consultant :: http://eliga.fi
Lead Developer of Robot Framework :: http://robotframework.org
Reply all
Reply to author
Forward
0 new messages