[autotest] Extract _to_kwargs function

BUG=None
TEST=None

Change-Id: Ib01ec5dee7f95f5d056a592cd2c335273cd1703c
Reviewed-on: https://chromium-review.googlesource.com/419085
Commit-Ready: Allen Li <ayatane@chromium.org>
Tested-by: Allen Li <ayatane@chromium.org>
Reviewed-by: Prathmesh Prabhu <pprabhu@chromium.org>
diff --git a/frontend/afe/rpc_utils.py b/frontend/afe/rpc_utils.py
index 3b02dec..fdb60a4 100644
--- a/frontend/afe/rpc_utils.py
+++ b/frontend/afe/rpc_utils.py
@@ -1317,32 +1317,8 @@
         Since frontend.RpcClient.run() takes only keyword arguments for
         an RPC, positional arguments of the RPC function need to be
         transformed to key-value pair (dictionary type).
-
-        inspect.getcallargs() is a useful utility to achieve the goal,
-        however, we need an additional effort when an RPC function has
-        **kwargs argument.
-        Let's say we have a following form of RPC function.
-
-        def rpcfunc(a, b, **kwargs)
-
-        When we call the function like "rpcfunc(1, 2, id=3, name='mk')",
-        inspect.getcallargs() returns a dictionary like below.
-
-        {'a':1, 'b':2, 'kwargs': {'id':3, 'name':'mk'}}
-
-        This is an incorrect form of arguments to pass to the rpc function.
-        Instead, the dictionary should be like this.
-
-        {'a':1, 'b':2, 'id':3, 'name':'mk'}
         """
-        funcargs = inspect.getcallargs(func, *args, **kwargs)
-        kwargs = dict()
-        for k, v in funcargs.iteritems():
-            if argspec.keywords and k == argspec.keywords:
-                kwargs.update(v)
-            else:
-                kwargs[k] = v
-
+        kwargs = _convert_to_kwargs_only(func, args, kwargs)
         if server_utils.is_shard():
             afe = frontend_wrappers.RetryingAFE(
                     server=server_utils.get_global_afe_hostname(),
@@ -1353,6 +1329,60 @@
     return replacement
 
 
+def _convert_to_kwargs_only(func, args, kwargs):
+    """Convert a function call's arguments to a kwargs dict.
+
+    This is best illustrated with an example.  Given:
+
+        def foo(a, b=None, **kwargs): pass
+
+    If foo is called like `foo(1, 2, c=3)`, this would correspond to:
+
+        args = (1, 2)
+        kwargs = {'c': 3}
+
+    Calling `_convert_to_kwargs(foo, (1, 2), {'c': 3})` would return:
+
+        {'a': 1, 'b': 2, 'c': 3}
+
+    This could then be passed to foo as a keyword arguments dict:
+
+        foo(**kwargs)
+
+    @param func: function whose signature to use
+    @param args: positional arguments of call
+    @param kwargs: keyword arguments of call
+
+    @returns: kwargs dict
+    """
+    # inspect.getcallargs() is a useful utility to achieve the goal,
+    # however, we need an additional effort when an RPC function has
+    # **kwargs argument.
+    # Let's say we have a following form of RPC function.
+    #
+    # def rpcfunc(a, b, **kwargs)
+    #
+    # When we call the function like "rpcfunc(1, 2, id=3, name='mk')",
+    # inspect.getcallargs() returns a dictionary like below.
+    #
+    # {'a':1, 'b':2, 'kwargs': {'id':3, 'name':'mk'}}
+    #
+    # This is an incorrect form of arguments to pass to the rpc function.
+    # Instead, the dictionary should be like this.
+    #
+    # {'a':1, 'b':2, 'id':3, 'name':'mk'}
+
+    argspec = inspect.getargspec(func)
+    funcargs = inspect.getcallargs(func, *args, **kwargs)
+    kwargs = dict()
+    for k, v in funcargs.iteritems():
+        if argspec.keywords and k == argspec.keywords:
+            kwargs.update(v)
+        else:
+            kwargs[k] = v
+    return kwargs
+
+
 def get_sample_dut(board, pool):
     """Get a dut with the given board and pool.