Python3: inject a recursive function into exec() in a function - python

In python3, if a function with recursive invoking is injected into exec() in a function, I got an error.
For example, below code
def B(pys):
exec(pys)
pys="""
def fibonacci(n):
if n == 1 or n == 2:
r = 1
else:
r = fibonacci(n - 1) + fibonacci(n - 2)
return r
print(fibonacci(3))
"""
B(pys)
will raise NameError.
$ py -3.8 testrecursivefun.py
Traceback (most recent call last):
File "testrecursivefun.py", line 14, in <module>
B(pys)
File "testrecursivefun.py", line 2, in B
exec(pys)
File "<string>", line 9, in <module>
File "<string>", line 6, in fibonacci
NameError: name 'fibonacci' is not defined
If I run exec(pys) directly under the module, the exception disappeared.
The reason has been described in another question How does exec work with locals?. But I still don't know how I can figure out the recursive invoking in exec(). Because the function name is dynamic for me. I cannot add it to locals() to exec(). Who can help me figure it out.

For the sake of an answer, you can wrap your code in a function so the recursive function is in its local scope:
import textwrap
def B(pys):
exec(pys, globals(), {})
pys="""
def fibonacci(n):
if n == 1 or n == 2:
r = 1
else:
r = fibonacci(n - 1) + fibonacci(n - 2)
return r
print(fibonacci(11))
"""
def wrap(s):
return "def foo():\n" \
"{}\n" \
"foo()".format(textwrap.indent(s, ' ' * 4))
B(wrap(pys))
Generally, reconsider using exec.

I actually got interested in your question, so I started researching on this topic. Seems like the simple solution to your problem is to:
First compile the string to code using compile function in python
Then execute the compiled code using exec function
Here is the sample solution:
psy="""
def fibonacci(n):
if n == 1 or n == 2:
r = 1
else:
r = fibonacci(n - 1) + fibonacci(n - 2)
return r
print(fibonacci(3))
"""
def B(psy):
code = compile(psy, '<string>', 'exec')
exec(code, globals())
B(psy)
Here compile takes three parameters:
First is the code in string format, Second is the filename hint which we used as we take string as code itself, and third can be one of 'exec', 'eval' and 'single'.
This link contains detail explanation of how you should use exec and eval in python. Do check them out for detail explanation.

Related

Python AST exec "... is not defined" error on recursive function

I came across this error
def test_rec():
import ast
exec(compile(ast.fix_missing_locations(ast.parse("""
def fact(n):
return 1 if n == 0 else n * fact(n - 1)
print(fact(5))
"""), "<string>", "exec")))
This yield this error, which is weird
Traceback (most recent call last):
File "/Users/gecko/.pyenv/versions/3.9.0/envs/lampy/lib/python3.9/site-packages/nose/case.py", line 198, in runTest
self.test(*self.arg)
File "/Users/gecko/code/lampycode/tests/test_let_lang.py", line 6, in test_rec
exec(compile(ast.fix_missing_locations(ast.parse("""
File "<string>", line 4, in <module>
File "<string>", line 3, in fact
NameError: name 'fact' is not defined
If I copy and paste the same code in REPL it works fine
>>> def fact(n):
... return 1 if n == 0 else n * fact(n - 1)
...
>>> print(fact(5))
120
>>>
Any ideas?
I could reduce the problem further here is the minimal exempla, this would overflow the stack but it gives me the same not defined error
def test_rec3():
exec("""
def f():
f()
f()
""")
--
Second edit, going even further, this only happens inside functions
This works
exec("""
def f(n):
print("end") if n == 1 else f(n-1)
f(10)""")
But this gives me the same error as above
def foo():
exec("""
def f(n):
print("end") if n == 1 else f(n-1)
f(10)""")
foo()
If you use exec with the default locals, then binding local variables is undefined behavior. That includes def, which binds the new function to a local variable.
Also, functions defined inside exec can't access closure variables, which fact would be.
The best way to avoid these problems is to not use exec. The second best way is to provide an explicit namespace:
namespace = {}
exec(whatever, namespace)

How can I do an eval correctly in Python?

I'm trying to evaluate a string in a Flask project, but I keep getting this error.
File "<string>", line 0
^
SyntaxError: unexpected EOF while parsing
This is the code I am using
def f(x):
input = "math.log((math.sin(x)**2) + 1) - (1 / 2)"
string = input.replace("x",str(x))
result = eval(string)
return result
Because replace is a method of the string class, it must be called from the string itself.
def f(x):
input = "math.log((math.sin(x)**2) + 1) - (1 / 2)"
string = input.replace("x",str(x))
result = eval(string)
return result
But, you should avoid calling eval unless you absolutely have to; eval is evil. Eval (and its cousin exec) can open up your program to arbitrary code injections.
Can you explain your usecase a little more and I can see if I can suggest a better alternative?
Why wouldn’t this work:
def f(x):
return math.log((math.sin(x)**2) + 1) - (1 / 2)
Try to change
result = eval(string)
return result
to
return eval(string)

Trouble with NameError in IDLE - Python

got some trouble with my selfmade code.
def even(a, b):
f = []
while a <= b:
if a % 2 == 0:
f.append(a)
a = a + 1
return f;
When i'm tryin to call it from the shell it says:
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
even(0,200)
NameError: name 'even' is not defined.
I think it's not a really tricky problem, but can u help my anyway?
Thank you up front.
Cheers
Two things. Are you indenting your function correctly and how are you calling your function? the following code works:
def even(a, b):
f = []
while a <= b:
if a % 2 == 0:
f.append(a)
a = a + 1
return f;
print(even(2,3))
#output: [2]
Notice the indentation and the fact that even is being called after it is defined.
Edit: I notice you've gotten it working, now would be a good time to refacter some parts of your function i.e. give the variable f a more descriptive name.
If you want to run it from your shell, you would want to:
enter python into shell, to start your python interpreter
enter from <your-code's-filename.py> import even
then you can use the function as you tried: even(0,200)
But you can also just run it from within IDLE like Wright suggests.

Different results when passing function / method to equation solver

This question is a follow-up of previous question pass class method to fsolve.
#jim's answer in that question about difference between the function object name and a function call clarified my confusion and solved the problem. However, when I tried similar things in sympy:
from sympy.solvers import solve
from sympy import Symbol
class Demo():
def __init__(self, var):
self.i = var
def func(self):
return self.i ** 2 - 4
x = Symbol('x')
def func(v):
return v ** 2 - 4
new = Demo(x)
solve(new.func(), x) # This works fine, even as a function call
solve(func(x), x) # This works fine, even as a function call
Why do I have different results? (In scipy I need to pass function name to solver while in sympy I need to pass the function call.) Is it because different implementation of the two libraries? In the above example, if I substitute the function call with function name, exception will be raised:
File "<ipython-input-26-3554c1f86646>", line 13, in <module>
solve(new.func, x)
File "Anaconda3\lib\site-packages\sympy\solvers\solvers.py", line 817, in solve
f, symbols = (_sympified_list(w) for w in [f, symbols])
File "Anaconda3\lib\site-packages\sympy\solvers\solvers.py", line 817, in <genexpr>
f, symbols = (_sympified_list(w) for w in [f, symbols])
File "Anaconda3\lib\site-packages\sympy\solvers\solvers.py", line 808, in _sympified_list
return list(map(sympify, w if iterable(w) else [w]))
File "Anaconda3\lib\site-packages\sympy\core\sympify.py", line 324, in sympify
raise SympifyError('could not parse %r' % a, exc)
SympifyError: Sympify of expression 'could not parse '<bound method Demo.func of <__main__.Demo object at 0x0000018A37DA6518>>'' failed, because of exception being raised:
SyntaxError: invalid syntax (<string>, line 1)
When the solver is provided parameter (func(x), x), it is equivalent to provide (x ** 2 - 4, x), the first part of which is an expression containing the declared symbol x.
However when the parameter list is (func, x), the variable in function definition, v, is undefined and does not match that provided as the second argument, which is x. This causes exception to be raised.
In the scipy case, returned expression of the function definition has one variable, var, to be solved, and can thus work properly.

Python multi-line expressions and stack trace

We have a simple AssertTrue function used in our python project and I wanted to modify the output it provides to print the code statement from which it was called. The code looks something like this:
1 import traceback
2
3 def AssertTrue(expr, reason=None):
4 print traceback.format_stack()[-2]
5
6 AssertTrue(1 == 2,
7 reason='One is not equal to two')
The output:
File "/tmp/fisken.py", line 7, in <module>
reason='One is not equal to two')
I'm wondering why traceback.format_stack only gives me the code on line 7. The statement starts on line 6 and the expression I would like to see in the output is also on that same line. Doesn't traceback handle multi-line function calls?
(Never mind that there are better ways to do AssertTrue(...). I'm just wondering why traceback.format_stack (and .extract_stack) does not behave as I expected it to)
Doesn't traceback handle multi-line function calls?
Many functions are tens or even (horrors) hundreds of lines long. If traceback did print the whole function, then stack traces would become incomprehensibly long. So I guess what you are seeing is an attempt to keep things clean and minimal.
I have pulled together some answers to similar questions:
Python code to get current function into a variable?
How can I get the source code of a Python function?
With the consideration that it inspect can only obtain the source for the whole function (if the source is available on the path) I can offer you this:
import traceback
import inspect
import gc
def giveupthefunc(frame):
code = frame.f_code
globs = frame.f_globals
functype = type(lambda: 0)
funcs = []
for func in gc.get_referrers(code):
if type(func) is functype:
if getattr(func, "func_code", None) is code:
if getattr(func, "func_globals", None) is globs:
funcs.append(func)
if len(funcs) > 1:
return None
return funcs[0] if funcs else None
def AssertTrue(expr, reason=None):
print traceback.format_stack()[-2]
frame = inspect.currentframe().f_back
func = giveupthefunc(frame)
if func:
source = inspect.getsourcelines(func)
i = source[1]
for line in source[0]:
print i, ":", line,
i += 1
def my_fun():
AssertTrue(1 == 2,
reason='One is not equal to two')
my_fun()
Which produces:
/Library/Frameworks/Python.framework/Versions/2.7/bin/python /Users/xxxx/Documents/PycharmProjects/scratchpad/test.py
File "/Users/xxxx/Documents/PycharmProjects/scratchpad/test.py", line 35, in my_fun
reason='One is not equal to two')
33 : def my_fun():
34 : AssertTrue(1 == 2,
35 : reason='One is not equal to two')

Categories

Resources