Split the function name from the arguments. Look up the function by name using a predefined map. Parse the arguments with literal_eval
. Call the function with the arguments.
available = {}
def register_func(f):
available[f.__name__] = f
@register_func
def var(value):
print(value)
from ast import literal_eval
def do_user_func(user_input):
name, args = user_input.split('(', 1)
return available[name](*literal_eval('(' + args[:-1] + ',)'))
do_user_func("var('test')") # prints "test"
This is still incredibly brittle, any invalid input will fail (such as forgetting parentheses, or an invalid function name). It's up to you to make this more robust.
literal_eval
is still somewhat unsafe on untrusted input, as it's possible to construct small strings that evaluate to large amounts of memory. '[' * 10 + ']' * 10
, for a safe but demonstrative example.
Finally, do not use eval
on untrusted user input. There is no practical way to secure it from malicious input. While it will evaluate the nice input you expect, it will also evaluate code that, for example, will delete all your files.
Any attempt to make eval
safe will end up being more complex than any of the solutions here, for no practical benefit. It will still not be safe in some way you didn't anticipate. Don't do it.