1

Is there a way to convert a string to a function in Python while maintaining indentation, newline characters etc.?

For eg., take in "def fn1():\n\tprint("Hello World")"

and convert it to a function:

def fn1():
    print("Hello World)

My use case is to allow a user to pass in a function defined by them as a JSON object which is then used in an otherwise static engine.

An example would be:

def run_user_code(fn_str):
    fn=convert_to_fn(fn_str)
    return fn # or call fn here like fn()

Similar to this question but in Python

str31
  • 23
  • 4
  • And... you want to call it from the same script? – Fiddling Bits Aug 11 '22 at 19:59
  • 5
    Can you explain why you want to do this? This sounds like an [XY problem](https://xyproblem.info/) to me. – 0x5453 Aug 11 '22 at 20:00
  • 1
    @0x5453 Updated with a use case – str31 Aug 11 '22 at 20:00
  • So you want a user to be able to execute arbitrary code? Sounds like a bad idea. – timgeb Aug 11 '22 at 20:00
  • Is the data (and its source) trustworthy? This may execute arbitrary functions and is dangerous. – Michael Butscher Aug 11 '22 at 20:02
  • @timgeb Valid concern, but the user would be running this on their own system so if they did want to write malicious code, they'd be able to even outside of my example. – str31 Aug 11 '22 at 20:03
  • @MichaelButscher yes the code can be expected to be safe to run – str31 Aug 11 '22 at 20:04
  • 1
    Well in that case it's juse `exec`. But again seems like a strange thing to want to do. – timgeb Aug 11 '22 at 20:04
  • 1
    If the user is running this on their own machine, and they're willing and able to write Python code, why don't you just give them a Python API that takes an actual function object, rather than an API that takes a JSON object that is meant to be interpreted as Python source code? – Samwise Aug 11 '22 at 20:30
  • How does the JSON Object look like? – noah1400 Aug 11 '22 at 20:45
  • @noah1400 it would be something like {"python_method":"def fn()\n\t..."} – str31 Aug 12 '22 at 16:05
  • @Samwise Interesting. Would it be possible to pass in a python method from a non-python environment in which the API is being called? For eg., from a java class that might be invoking the python code. – str31 Aug 12 '22 at 16:07

3 Answers3

3

You can use the python module ast

import ast
def string_to_function(string):
    # parse the string to an ast
    tree = ast.parse(string)
    # get function name
    name = tree.body[0].name # Needs to be changed depending on the input
    # compile the ast to executable code
    code = compile(tree, '<string>', 'exec')
    ns = {}
    # exec the code in a namespace
    exec(code, ns)
    # return the callable function
    return ns[name]

s = "def fn1():\n\tprint('Hello World')"
fn = string_to_function(s)
fn()

Output

Hello World

This code only works if the input is a function.

Or you can just call exec().

c = "def fn2():\n\tprint('Hello World')"
exec(c)
fn2()

This only works if you already know the function name.

Alwayss be careful what code you execute. In this example the User can put any code inside his function, including imports like os or sys, and it will be executed. This can be very dangerous!

noah1400
  • 1,282
  • 1
  • 4
  • 15
1

A variation of the answer by noah1400. You can get a dict with a single entry, where the key is the name of the function and its associated value is the function:

string = "def fn2():\n\tprint('Hello World')"
glob = dict()
local = dict()
exec(string, glob, local)
name = list(local)[0]
func = local[name]
print(name)
func()

Result:

fn2
Hello World
Robert Haas
  • 615
  • 2
  • 8
0

It's a bad idea, but you can execute the string as code with

exec('def fn1():\n\tprint("Hello World")')

which defines the function.

How do I execute a string containing Python code in Python?

Addison Schmidt
  • 374
  • 1
  • 7