How to extract a function given by a content string in Python - python

I have a content of a function given by a string. I'm looking for a way how to convert this string into a function object to call it.
content = "def my_function(x):\n return x**2"
my_function = extract_function_from_string(content) # extract_function_from_string ???
print my_function(5) # prints 25
Perhaps, there's a way to convert the string into a python module and extract the function from it

If you know the function name, you can run exec on the string. Eval will not work as it only targets expressions. See \r\n vs \n in python eval function. If you don't know the function name, but all your function have the same format you can try something like this:
def extract_function_from_string(content):
exec(content)
start = 4
end = content.find("(")
function_name = content[start:end]
return eval(function_name)
my_function = extract_function_from_string("def my_function(x):\n return x**2")
print my_function(5)
The train has left the safe and sound python station a long time ago, but I guess this is just a curiosity.

You can use exec
>>>content = "def my_function(x):\n return x**2"
>>>exec content
>>>my_function(5)
25
For Python 3.x
>>>exec(content)

Try the exec statement:
>>> content = "def my_function(x):\n return x**2"
>>> exec content
>>> my_function(5)
25

Here is something that does not leave safe train of python completely.
def extract_function_from_string(content, function_index=0):
import ast
tree = ast.parse(content, mode='exec')
function = tree.body[function_index]
module = ast.Module([function])
exec(compile(module, filename="<ast>", mode='exec'))
return locals()[function.name]
content = "def my_function(x):\n return x**2"
my_function = extract_function_from_string(content) # extract_function_from_string ???
print(my_function(5)) # prints 25

You can use the eval function to do this. e.g. eval("print('Hello')").
However, do be advised that this can be a security risk, and there is probably a better way of doing it than parsing a string.

a = "lambda x:x**2"
my_func = eval(a)
print my_func(3)

Related

How to work in a function on a variable from outside a function - based on it's name as a string

How convert string to a Python object?
I have for example simple function:
def test_function(name):
str(name) + "insert(1, "test_01")
...
test_function("list1")
I want this to perform "list1.insert(1, "test_01) operation but it doesn't work. I guees there is a problem that it reads name as a string not as an object. How can I solve this out?
locals()"YourFunction"
or
globals()["YourFunction"]()
as an example:
def foo(number):
return number*10
print (globals()["foo"](10))
Output= 100
Thanks
I don't know if it's what you really want. But i think it's because of the quotation mark. See below:
def test_function(name):
print(str(name) + 'insert(1, "test_01")')
...
test_function("list1")
NOTE
By any means don't use eval, it is insecure ("eval is evil").
For more details about eval harmfulness read here.
Solution Requested
Using eval you can evaluate string that contains code and execute it.
The code here that is being evaluated, looks for the variable name in the global scope (outside the function).
The globals() dictionary contains the variables defined outside the function.
def test_function(name):
eval(f'(globals()["{name}"]).insert(1, "test_01")')
name = "list1"
list1 = []
test_function(name)
# Outside function
# eval(str(name) + '.insert(1, "test_01")')
print(list1)

Get call string for a Python function, as typed on the command line

I would like to store the exact string that was typed for a function call, from the function itself using introspection (I cannot/don't want to hack the command line interpreter -- so, for example getting history from readline or whatever is not what I am looking for).
Let's say, if user typed:
>>> myfunc('a', 'b', 1, mykwarg='hello')
I would like to get the call string (i.e. myfunc('a', 'b', 1, mykwarg='hello'))
from the code inside myfunc.
I can craft something like this:
def myfunc(a,b,c,mykwarg=None):
frame = inspect.currentframe()
sig = inspect.signature(myfunc)
args = []
for param in sig.parameters.values():
if param.name in frame.f_locals:
args.append(f"{param.name}={str(frame.f_locals[param.name])}")
cmd = f"{frame.f_code.co_name}({','.join(args)})"
print(cmd)
I get:
>>> myfunc('a', 'b', 1, mykwarg='hello')
myfunc(a=a,b=b,c=1,mykwarg=hello)
Which is not exactly what user typed. Also, I hope there is something more
robust and less 'hackish' to try...
Use case: I want to be able to associate a command call from my library with its result. I do not want to hard-code the command call storage for each function, I would prefer to use a decorator or something like that. This is probably much easier to do from the REPL, but I would like to not depend on it (like, if user calls the function from its own program, it should still be able to associate the command call with the result).
Finally I answer my own question, hopefully it can help someone else one day.
I decided to try to go the dis way, ie. "disassembling" the Python code object of
the outer frame calling my function, to see how it has been called to be able to
reconstruct the command line:
import dis
import inspect
import io
import ast
import re
def get_function_call_string():
in_func = False
args=[]
kwargs=[]
func_name = inspect.stack()[1][3]
frame = inspect.currentframe().f_back.f_back
dis_output = io.StringIO()
dis.dis(frame.f_code, file=dis_output)
for line in dis_output.getvalue().split('\n'):
instr = re.findall(r'^.*\s([A-Z_]+)\s*', line)[0]
if instr.startswith("CALL_FUNCTION") and in_func:
break
elif instr.startswith('LOAD_'):
name = re.findall(r'^.*\s\((.+)\)$', line)[0]
if in_func:
if name.startswith('('):
kwargs = ast.literal_eval(name)
else:
args.append(name)
elif name == func_name:
in_func = True
kwargs = [f"{a}={args.pop()}" for a in kwargs]
return f"{func_name}({', '.join(args)}{', ' if kwargs else ''}{', '.join(kwargs)})"
Example:
>>> def f(a,b,arg="toto"):
print(get_function_call_string())
>>> f(1,"test","example")
f(1, 'test', 'example')
>>> f(1, "test", arg="hello")
(1, 'test', arg='hello')
It is not a complete answer, since it cannot handle some argument types like
dict or list... I maybe will continue on this or change my mind and do differently.

How to change a string in a function when calling a function?

I'm not sure if this is possible but is there a way to change a string that a function prints while calling it from another function? I want to do something like this:
def string():
print ("This cat was scared.")
def main():
for words in string():
str.replace("cat", "dog")
# Print "The do was scared."
main()
By popular demand (well, one person's curiosity…), here's how you actually could change the string in a function before calling that function.
You should never do this in practice. There are some use cases for playing around with code objects, but this really isn't one of them. Plus, if you do anything less trivial, you should use a library like bytecode or byteplay instead of doing it manually. Also, it goes without saying that not all Python implementations use CPython-style code objects. But anyway, here goes:
import types
def string():
print ("This cat was scared.")
def main():
# A function object is a wrapper around a code object, with
# a bit of extra stuff like default values and closure cells.
# See inspect module docs for more details.
co = string.__code__
# A code object is a wrapper around a string of bytecode, with a
# whole bunch of extra stuff, including a list of constants used
# by that bytecode. Again see inspect module docs. Anyway, inside
# the bytecode for string (which you can read by typing
# dis.dis(string) in your REPL), there's going to be an
# instruction like LOAD_CONST 1 to load the string literal onto
# the stack to pass to the print function, and that works by just
# reading co.co_consts[1]. So, that's what we want to change.
consts = tuple(c.replace("cat", "dog") if isinstance(c, str) else c
for c in co.co_consts)
# Unfortunately, code objects are immutable, so we have to create
# a new one, copying over everything except for co_consts, which
# we'll replace. And the initializer has a zillion parameters.
# Try help(types.CodeType) at the REPL to see the whole list.
co = types.CodeType(
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize, co.co_flags, co.co_code,
consts, co.co_names, co.co_varnames, co.co_filename,
co.co_name, co.co_firstlineno, co.co_lnotab,
co.co_freevars, co.co_cellvars)
string.__code__ = co
string()
main()
If that's not hacky enough for you: I mentioned that code objects are immutable. And of course so are strings. But deep enough under the covers, they're just pointer to some C data, right? Again, only if we're using CPython, but if we are…
First, grab my superhackyinternals project off GitHub. (It's intentionally not pip-installable because you really shouldn't be using this except to experiment with your local build of the interpreter and the like.) Then:
import ctypes
import internals
def string():
print ("This cat was scared.")
def main():
for c in string.__code__.co_consts:
if isinstance(c, str):
idx = c.find('cat')
if idx != -1:
# Too much to explain here; see superhackyinternals
# and of course the C API docs and C source.
p = internals.PyUnicodeObject.from_address(id(c))
assert p.compact and p.ascii
length = p.length
addr = id(c) + internals.PyUnicodeObject.utf8_length.offset
buf = (ctypes.c_int8 * 3).from_address(addr + idx)
buf[:3] = b'dog'
string()
main()
As a guess:
You wanted string() to return a value the caller can use, instead of just printing something to the screen. So you need a return statement instead of a print call.
You want to loop over all of the words in that returned string, not all the characters, so you need to call split() on the string.
You want to replace stuff in each word, not in the literal "cat". So, you need to call replace on word, not on the str class. Also, replace doesn't actually change the word, it returns a new one, which you have to remember.
You want to print out each of those words.
If so:
def string():
return "This cat was scared."
def main():
for word in string().split():
word = word.replace("cat", "dog")
print(word, end=' ')
print()
main()
That fixes all of your problems. However, it can be simplified, because you don't really need word.replace here. You're swapping out the entire word, so you can just do this:
def main():
for word in string().split():
if word == "cat": word = "dog"
print(word, end=' ')
print()
But, even more simply, you can just call replace on the entire string, and you don't need a loop at all:
def main():
print(string().replace("cat", "dog"))
What I think you may actually be looking for, is the ability to call your function with a default argument:
def string(animal='cat'):
print("This {} was scared.".format(animal))
>>> string()
This cat was scared.
>>> string('dog')
This dog was scared.
If you pass nothing to string, the default value is assumed. Otherwise, the string prints with the substring you explicitly passed.

How to assign print output to a variable?

How to assign the output of the print function (or any function) to a variable?
To give an example:
import eyeD3
tag = eyeD3.Tag()
tag.link("/some/file.mp3")
print tag.getArtist()
How do I assign the output of print tag.getArtist to a variable?
The print statement in Python converts its arguments to strings, and outputs those strings to stdout. To save the string to a variable instead, only convert it to a string:
a = str(tag.getArtist())
To answer the question more generaly how to redirect standard output to a variable ?
do the following :
from io import StringIO
import sys
result = StringIO()
sys.stdout = result
result_string = result.getvalue()
If you need to do that only in some function do the following :
old_stdout = sys.stdout
# your function containing the previous lines
my_function()
sys.stdout = old_stdout
probably you need one of str,repr or unicode functions
somevar = str(tag.getArtist())
depending which python shell are you using
You can use parameter file to redirect output of print function
from io import StringIO
s = StringIO()
print(42, file=s)
result = s.getvalue()
somevar = tag.getArtist()
http://docs.python.org/tutorial/index.html
This is a standalone example showing how to save the output of a user-written function in Python 3:
from io import StringIO
import sys
def print_audio_tagging_result(value):
print(f"value = {value}")
tag_list = []
for i in range(0,1):
save_stdout = sys.stdout
result = StringIO()
sys.stdout = result
print_audio_tagging_result(i)
sys.stdout = save_stdout
tag_list.append(result.getvalue())
print(tag_list)
Output
['value = 0\n']
In Python 3.x, you can assign print() statement to the variable like this:
>>> var = print('some text')
some text
>>> var
>>> type(var)
<class 'NoneType'>
According to the documentation,
All non-keyword arguments are converted to strings like str() does and written to the stream, separated by sep and followed by end. Both sep and end must be strings; they can also be None, which means to use the default values. If no objects are given, print() will just write end.
The file argument must be an object with a write(string) method; if it is not present or None, sys.stdout will be used. Since printed arguments are converted to text strings, print() cannot be used with binary mode file objects. For these, use file.write(...) instead.
That's why we cannot assign print() statement values to the variable. In this question you have ask (or any function). So print() also a function with the return value with None. So the return value of python function is None. But you can call the function(with parenthesis ()) and save the return value in this way.
>>> var = some_function()
So the var variable has the return value of some_function() or the default value None. According to the documentation about print(), All non-keyword arguments are converted to strings like str() does and written to the stream. Lets look what happen inside the str().
Return a string version of object. If object is not provided, returns the empty string. Otherwise, the behavior of str() depends on whether encoding or errors is given, as follows.
So we get a string object, then you can modify the below code line as follows,
>>> var = str(some_function())
or you can use str.join() if you really have a string object.
Return a string which is the concatenation of the strings in iterable. A TypeError will be raised if there are any non-string values in iterable, including bytes objects. The separator between elements is the string providing this method.
change can be as follows,
>>> var = ''.join(some_function()) # you can use this if some_function() really returns a string value

Get functions called in a Python expression

I have a database that holds the name of Python functions and a string for their code. I want the user to be able to enter a Python code and see the result. The problem is that I need to know the names of the functions they call in order to retrieve the code from the database. For instance, if they enter cubic_fit(1, 2, get_data()), I need a way to get the function names cubic_fit and get_data. Is there a good way to do this?
The built-in function compile will do that for you exactly:
>>> compile("cubic_fit(1, 2, get_data())", '<string>', 'eval').co_names
('cubic_fit', 'get_data')
And it is safe to run. No code is actually being executed just compiled.
A quick example to you started. Note that you'll be expecting valid python semantics for this to work.
You can extend this to also parse your arguments...
import token, tokenize, StringIO
def extract_names(src):
rawstr = StringIO.StringIO(unicode(src))
tokens = tokenize.generate_tokens(rawstr.readline)
for i, item in enumerate(tokens):
toktype, toktext, (srow,scol), (erow,ecol), line = item
if token.tok_name[toktype] == 'NAME':
print 'name:', toktext
extract_names("cubic_fit(1, 2, get_data())")
# --> output:
# name: cubic_fit
# name: get_data
If you just want the names, then the compile() and co_names method will work best.
You also might take advantage of the capability of eval() to use any mapping object as its locals parameter. You could create a mapping object to look up and compile the objects from your database as needed by eval().
Example:
class LookitUp(object):
def __init__(self):
# simulate some data
self.d = { "foo": "def foo(a):\n return a + 2"}
def __getitem__(self,key):
localdict = {}
c = compile(self.d.get(key,""),"<string>","exec")
eval(c,globals(),localdict)
return localdict[key]
d = LookitUp()
def bar(a):
return a - 1
print "foo from database :",eval("foo(3)",globals(), d)
print "bar from globals():",eval("bar(3)",globals(), d)
print "foo(bar(3)) :",eval("foo(bar(3))",globals(), d)
Result:
foo from database : 5
bar from globals(): 2
foo(bar(3)) : 4
You may need to modify based on what your source in the database looks like, but it's a place to start.

Categories

Resources