Tuesday, April 19, 2016

Call me! PythonJS and variadic functions

I’m currently developing UI automation tests for Adobe Experience Design, aka Adobe XD. Or, for anybody who hasn’t heard of the recent name change: Project Comet!

Combine and attach different material 

1. This is a note to myself. Mostly

Today, I got stuck in something that looked pretty straight forward to me. I wanted to call a function in a Python API from my JavaScript code. We use PythonJS to make this magic happen. In the most cases, calling a function in the Python API is not a big deal. Scalar values work well, named arguments work well, but variadic function args do not.
The function I wanted to call looks like this:

def menuItem(self, *args)
Calling the function from JavaScript, I tried several things. The most promising for me was to convert the JavaScript arguments into an Array and pass it on.

pythonObj.menuItem(['value1', 'value2', 'value3'])
The result was an error, telling me that I should call the function with a string or int. Okay, the function signature accept either one or multiple `int`s or `string`s. In various combinations.
I guess, my initial approach was a bit too naive. A JavaScript array doesn’t translate to a vararg in Python. Got it! But how do I pass the list of varargs from JavaScript to Python? I tried to apply the arguments from the JavaScript function to obj.menuItem. But this didn’t work with the same error.
I tried to call the Python function by

obj.menuItem("value1", "value2", "value3")
That worked well. But I can’t make this happen from the JavaScript side? How can I split up an unknown number of arguments and pass them all as single arguments? I had no answer for that.

2. Workaround, Hack, you name it

If I would have a fixed number of arguments, the solution would be easier. I ended up with a hacky solution (IMHO), since I didn’t wanted to spent more time on this interesting but blocking issue.

function callPythonFunction(args) {
    switch (args.length) {
        case 1: {
            return app.menuItem(args[0]);
        }

        case 2: {
            return app.menuItem(args[0], args[1]);
        }

        case 3: {
            return app.menuItem(args[0], args[1], args[2]);
        }

        default:
            console.error("Unsupported amount of arguments for args", args.length);
        }
}
Is this a good solution? Until I find something better, I consider it as a good solution.

3. Summary

Bridging languages and make up for missing or non transferrable features requires creative solutions. They might not be the best solutions, but they unblock and let you move on.

What do you think? Is there a better way of doing this? Let me know in the comments.

Thanks for reading!

No comments: