User defined function through inputs in Python

122 views Asked by At

I wish to create a custom calculator where the user defines two parameters and a function using a GUI and when they click on calculate it executes their user defined function passing the two parameters.

argument1 = IntSlider( … )
argument2 = IntSlider( … )
userDefinedFunction = TextArea( … )
calculateButton = Button ( … )
calculateButton.on_click(userDefinedFunction)

So that let’s say somebody defines :

  1. argument1 = 3
  2. argument2 = 4
  3. userDefinedFunction = def udf(arg1,arg2): return arg1**2 + arg2**2

Would return 25 as 3*3 + 4*4 = 25.

2

There are 2 answers

0
flakes On BEST ANSWER

I'd probably go with something more limiting than a full function definition. Having the user create the function signature is going to add complications as you cannot eval it, you would have to exec it instead. Then finding out the method name would be complex, and it would allow the user to overwrite local variables, or do other imports, which might not be desirable.

An easier way could be to expect the user to complete the lambda method lambda arg1, arg2: <user code input>.

Note that eval and exec (or running any unvalidated user input as code for that matter) are dangerous. If the user is running this on their local machine only this is somewhat okay, but do not do this if the inputs are coming from external sources, like for example, a web server.

argument1 = 3
argument2 = 4
user_func_input = "arg1**2 + arg2**2"
user_func = eval(f"lambda arg1, arg2: {user_func_input}")

print(user_func(argument1, argument2))
25
2
Armando Leopoldo Keller On

Using eval or exec for user input data is dangerous. Any valid syntax will be evaluated by the interpreter. The secure way is to verify if the operation is valid and then execute it.

There are plenty of algorithms to distinguish numbers from operators (search for infix notation).

To safely execute the operations, you could use the following functions

# For python 3.10 and above (with support for match statement)
def apply_function(argument1:int, argument2:int, function:str)->float:
    match function:
        case "add" | "sum"| "+":
            return argument1 + argument2
        case "sub" | "subtract" | "-":
            return argument1 - argument2
        case "mul" | "multiply" | "*" | "x" | "times":
            return argument1 * argument2
        case "div" | "divide" | "/":
            return argument1 / argument2
        case "pow" | "power" | "^"|"**":
            return argument1 ** argument2
        case _:
            raise ValueError("Invalid function")

# For python 3.9 and below (without support for match statement)
def apply_function_v2(argument1:int, argument2:int, function:str)->float:
    if function in ("add", "sum", "+"):
        return argument1 + argument2
    elif function in ("sub", "subtract", "-"):
        return argument1 - argument2
    elif function in ("mul", "multiply", "*", "x", "times"):
        return argument1 * argument2
    elif function in ("div", "divide", "/"):
        return argument1 / argument2
    elif function in ("pow", "power", "^", "**"):
        return argument1 ** argument2
    else:
        raise ValueError("Invalid function")