Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-asteval for openSUSE:Factory checked in at 2021-02-18 20:40:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-asteval (Old) and /work/SRC/openSUSE:Factory/.python-asteval.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-asteval" Thu Feb 18 20:40:32 2021 rev:9 rq:873194 version:0.9.22 Changes: -------- --- /work/SRC/openSUSE:Factory/python-asteval/python-asteval.changes 2020-01-18 12:18:30.887151253 +0100 +++ /work/SRC/openSUSE:Factory/.python-asteval.new.28504/python-asteval.changes 2021-02-18 20:52:39.943453745 +0100 @@ -1,0 +2,8 @@ +Wed Feb 17 11:57:46 UTC 2021 - Ben Greiner <[email protected]> + +- Update to 0.9.22 + * another important but small fix for Python 3.9 + * Merge branch 'nested_interrupts_returns' +- Drop hard numpy requirement, don't test on python36 + +------------------------------------------------------------------- Old: ---- asteval-0.9.18.tar.gz New: ---- asteval-0.9.22.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-asteval.spec ++++++ --- /var/tmp/diff_new_pack.85wBqX/_old 2021-02-18 20:52:40.583454346 +0100 +++ /var/tmp/diff_new_pack.85wBqX/_new 2021-02-18 20:52:40.587454350 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-asteval # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,10 +16,10 @@ # +%{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 -%{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-asteval -Version: 0.9.18 +Version: 0.9.22 Release: 0 Summary: Safe, minimalistic evaluator of python expression using ast module License: MIT @@ -29,13 +29,10 @@ BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-numpy >= 1.6 -Requires: python-six BuildArch: noarch # SECTION test requirements -BuildRequires: %{python_module numpy >= 1.6} BuildRequires: %{python_module pytest} -BuildRequires: %{python_module six} +BuildRequires: %{python_module numpy if (%python-base without python36-base)} # /SECTION %python_subpackages ++++++ asteval-0.9.18.tar.gz -> asteval-0.9.22.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/.gitattributes new/asteval-0.9.22/.gitattributes --- old/asteval-0.9.18/.gitattributes 1970-01-01 01:00:00.000000000 +0100 +++ new/asteval-0.9.22/.gitattributes 2021-02-11 22:05:29.000000000 +0100 @@ -0,0 +1 @@ +asteval/_version.py export-subst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/.gitignore new/asteval-0.9.22/.gitignore --- old/asteval-0.9.18/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/asteval-0.9.22/.gitignore 2021-02-11 22:05:29.000000000 +0100 @@ -0,0 +1,12 @@ +*.pyc +*~ +*# +.coverage +NonGit/ +doc/_build +doc/*.pdf +build +dist +*.egg-info + +MANIFEST diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/INSTALL new/asteval-0.9.22/INSTALL --- old/asteval-0.9.18/INSTALL 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/INSTALL 2021-02-11 22:05:29.000000000 +0100 @@ -4,13 +4,12 @@ To install the asteval module, use:: python setup.py install -or +or pip install asteval Asteval require Python 3.5 or higher. -If installed, many functions and constants from numpy +If installed, many functions and constants from numpy will be used by default. Matt Newville <[email protected]> Last Update: 14-Nov-2019 - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/LICENSE new/asteval-0.9.22/LICENSE --- old/asteval-0.9.18/LICENSE 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/LICENSE 2021-02-11 22:05:29.000000000 +0100 @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2019 Matthew Newville, The University of Chicago +Copyright (c) 2021 Matthew Newville, The University of Chicago Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/MANIFEST.in new/asteval-0.9.22/MANIFEST.in --- old/asteval-0.9.18/MANIFEST.in 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/MANIFEST.in 2021-02-11 22:05:29.000000000 +0100 @@ -1,8 +1,8 @@ -include README.txt INSTALL LICENSE MANIFEST.in PKG-INFO +include README.txt INSTALL LICENSE MANIFEST.in PKG-INFO include setup.py publish_docs.sh exclude *.pyc core.* *~ *.pdf recursive-include lib *.py -recursive-include tests *.py +recursive-include tests *.py recursive-include doc * recursive-exclude doc/_build * recursive-exclude doc *.pdf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/PKG-INFO new/asteval-0.9.22/PKG-INFO --- old/asteval-0.9.18/PKG-INFO 2019-12-17 18:36:53.000000000 +0100 +++ new/asteval-0.9.22/PKG-INFO 2021-02-11 22:05:48.076786500 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: asteval -Version: 0.9.18 +Version: 0.9.22 Summary: Safe, minimalistic evaluator of python expression using ast module Home-page: http://github.com/newville/asteval Author: Matthew Newville @@ -22,4 +22,4 @@ Classifier: Intended Audience :: Science/Research Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Requires-Python: >=3.5 +Requires-Python: >=3.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/README.rst new/asteval-0.9.22/README.rst --- old/asteval-0.9.18/README.rst 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/README.rst 2021-02-11 22:05:29.000000000 +0100 @@ -7,6 +7,12 @@ .. image:: https://codecov.io/gh/newville/asteval/branch/master/graph/badge.svg :target: https://codecov.io/gh/newville/asteval +.. image:: https://img.shields.io/pypi/v/asteval.svg + :target: https://pypi.org/project/asteval + +.. image:: https://img.shields.io/pypi/dm/asteval.svg + :target: https://pypi.org/project/asteval + .. image:: https://zenodo.org/badge/4185/newville/asteval.svg :target: https://zenodo.org/badge/latestdoi/4185/newville/asteval diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/asteval/__init__.py new/asteval-0.9.22/asteval/__init__.py --- old/asteval-0.9.18/asteval/__init__.py 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/asteval/__init__.py 2021-02-11 22:05:29.000000000 +0100 @@ -17,10 +17,10 @@ The University of Chicago """ -from .asteval import Interpreter -from .astutils import (NameFinder, valid_symbol_name, make_symbol_table, - get_ast_names) from ._version import get_versions +from .asteval import Interpreter +from .astutils import (NameFinder, get_ast_names, make_symbol_table, + valid_symbol_name) __all__ = ['Interpreter', 'NameFinder', 'valid_symbol_name', 'make_symbol_table', 'get_ast_names'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/asteval/_version.py new/asteval-0.9.22/asteval/_version.py --- old/asteval-0.9.18/asteval/_version.py 2019-12-17 18:36:53.000000000 +0100 +++ new/asteval-0.9.22/asteval/_version.py 2021-02-11 22:05:48.076786500 +0100 @@ -8,11 +8,11 @@ version_json = ''' { - "date": "2019-12-17T11:34:21-0600", + "date": "2021-02-11T14:59:23-0600", "dirty": false, "error": null, - "full-revisionid": "2f796e3560c0e51df52ce8ca8062f2c379452107", - "version": "0.9.18" + "full-revisionid": "3312602c567038e6430b9680075431ab3814b199", + "version": "0.9.22" } ''' # END VERSION_JSON diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/asteval/asteval.py new/asteval-0.9.22/asteval/asteval.py --- old/asteval-0.9.18/asteval/asteval.py 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/asteval/asteval.py 2021-02-11 22:05:29.000000000 +0100 @@ -17,7 +17,7 @@ in the symbol table. The result is a restricted, simplified version of Python meant for -numerical caclulations that is somewhat safer than 'eval' because many +numerical calculations that is somewhat safer than 'eval' because many unsafe operations (such as 'import' and 'eval') are simply not allowed. Many parts of Python syntax are supported, including: @@ -37,20 +37,12 @@ 'getattr' for example) """ import ast -import time import inspect -from sys import exc_info, stdout, stderr, version_info +import time +from sys import exc_info, stderr, stdout -from .astutils import (UNSAFE_ATTRS, HAS_NUMPY, make_symbol_table, numpy, - op2func, ExceptionHolder, ReturnedNone, - valid_symbol_name) - -if version_info[0] < 3 or version_info[1] < 5: - raise SystemError("Python 3.5 or higher required") - -builtins = __builtins__ -if not isinstance(builtins, dict): - builtins = builtins.__dict__ +from .astutils import (HAS_NUMPY, UNSAFE_ATTRS, ExceptionHolder, ReturnedNone, + make_symbol_table, numpy, op2func, valid_symbol_name) ALL_NODES = ['arg', 'assert', 'assign', 'attribute', 'augassign', 'binop', 'boolop', 'break', 'call', 'compare', 'continue', 'delete', @@ -60,7 +52,8 @@ 'pass', 'raise', 'repr', 'return', 'slice', 'str', 'subscript', 'try', 'tuple', 'unaryop', 'while', 'constant'] -class Interpreter(object): + +class Interpreter: """create an asteval Interpreter: a restricted, simplified interpreter of mathematical expressions using Python syntax. @@ -93,7 +86,7 @@ no_listcomp : bool whether to support list comprehension. no_augassign : bool - whether to support augemented assigments (`a += 1`, etc). + whether to support augemented assignments (`a += 1`, etc). no_assert : bool whether to support `assert`. no_delete : bool @@ -203,7 +196,6 @@ """set node handler""" self.node_handlers[node] = handler - def user_defined_symbols(self): """Return a set of symbols that have been added to symtable after construction. @@ -238,7 +230,7 @@ if len(self.error) > 0 and not isinstance(node, ast.Module): msg = '%s' % msg err = ExceptionHolder(node, exc=exc, msg=msg, expr=expr, lineno=lineno) - self._interrupt = ast.Break() + self._interrupt = ast.Raise() self.error.append(err) if self.error_msg is None: self.error_msg = "at expr='%s'" % (self.expr) @@ -274,6 +266,10 @@ out = None if len(self.error) > 0: return out + if self.retval is not None: + return self.retval + if isinstance(self._interrupt, (ast.Break, ast.Continue)): + return self._interrupt if node is None: return out if isinstance(node, str): @@ -300,7 +296,7 @@ except: if with_raise: if len(self.error) == 0: - # Unhandled exception that didn't go through raise_exception + # Unhandled exception that didn't use raise_exception self.raise_exception(node, expr=expr) raise @@ -357,7 +353,7 @@ return self.run(node.value) # ('value',) def on_return(self, node): # ('value',) - """Return statement: look for None, return special sentinal.""" + """Return statement: look for None, return special sentinel.""" self.retval = self.run(node.value) if self.retval is None: self.retval = ReturnedNone @@ -376,7 +372,7 @@ def on_expression(self, node): "basic expression" - return self.on_module(node) # ():('body',) + return self.on_module(node) # ():('body',) def on_pass(self, node): """Pass statement.""" @@ -403,7 +399,8 @@ def on_assert(self, node): # ('test', 'msg') """Assert statement.""" if not self.run(node.test): - self.raise_exception(node, exc=AssertionError, msg=node.msg) + msg = node.msg.s if node.msg else "" + self.raise_exception(node, exc=AssertionError, msg=msg) return True def on_list(self, node): # ('elt', 'ctx') @@ -416,8 +413,8 @@ def on_dict(self, node): # ('keys', 'values') """Dictionary.""" - return dict([(self.run(k), self.run(v)) for k, v in - zip(node.keys, node.values)]) + return {self.run(k): self.run(v) for k, v in + zip(node.keys, node.values)} def on_constant(self, node): # ('value', 'kind') """Return constant value.""" @@ -431,10 +428,6 @@ """Return string.""" return node.s - def on_nameconstant(self, node): # ('value',) - """named constant""" - return node.value - def on_name(self, node): # ('id', 'ctx') """Name node.""" ctx = node.ctx.__class__ @@ -448,7 +441,7 @@ self.raise_exception(node, exc=NameError, msg=msg) def on_nameconstant(self, node): - """ True, False, None in python >= 3.4 """ + """True, False, or None""" return node.value def node_assign(self, node, val): @@ -459,7 +452,8 @@ """ if node.__class__ == ast.Name: - if not valid_symbol_name(node.id) or node.id in self.readonly_symbols: + if (not valid_symbol_name(node.id) or + node.id in self.readonly_symbols): errmsg = "invalid symbol name (reserved word?) %s" % node.id self.raise_exception(node, exc=NameError, msg=errmsg) self.symtable[node.id] = val @@ -474,14 +468,8 @@ setattr(self.run(node.value), node.attr, val) elif node.__class__ == ast.Subscript: - sym = self.run(node.value) - xslice = self.run(node.slice) - if isinstance(node.slice, ast.Index): - sym[xslice] = val - elif isinstance(node.slice, ast.Slice): - sym[slice(xslice.start, xslice.stop)] = val - elif isinstance(node.slice, ast.ExtSlice): - sym[xslice] = val + self.run(node.value)[self.run(node.slice)] = val + elif node.__class__ in (ast.Tuple, ast.List): if len(val) == len(node.elts): for telem, tval in zip(node.elts, val): @@ -501,7 +489,7 @@ return delattr(sym, node.attr) # ctx is ast.Load - fmt = "cannnot access attribute '%s' for %s" + fmt = "cannot access attribute '%s' for %s" if node.attr not in UNSAFE_ATTRS: fmt = "no attribute '%s' for %s" try: @@ -544,10 +532,7 @@ nslice = self.run(node.slice) ctx = node.ctx.__class__ if ctx in (ast.Load, ast.Store): - if isinstance(node.slice, (ast.Index, ast.Slice, ast.Ellipsis)): - return val.__getitem__(nslice) - elif isinstance(node.slice, ast.ExtSlice): - return val[nslice] + return val[nslice] else: msg = "subscript with unknown context" self.raise_exception(node, msg=msg) @@ -561,8 +546,8 @@ while tnode.__class__ == ast.Attribute: children.append(tnode.attr) tnode = tnode.value - - if tnode.__class__ == ast.Name and tnode.id not in self.readonly_symbols: + if (tnode.__class__ == ast.Name and + tnode.id not in self.readonly_symbols): children.append(tnode.id) children.reverse() self.symtable.pop('.'.join(children)) @@ -598,7 +583,8 @@ rval = self.run(rnode) ret = op2func(op)(lval, rval) results.append(ret) - if (self.use_numpy and not isinstance(ret, numpy.ndarray)) and not ret: + if ((self.use_numpy and not isinstance(ret, numpy.ndarray)) and + not ret): break lval = rval if len(results) == 1: @@ -697,7 +683,7 @@ for hnd in node.handlers: htype = None if hnd.type is not None: - htype = builtins.get(hnd.type.id, None) + htype = __builtins__.get(hnd.type.id, None) if htype is None or isinstance(e_type(), htype): self.error = [] if hnd.name is not None: @@ -741,14 +727,21 @@ keywords = {} if func == print: keywords['file'] = self.writer - for key in node.keywords: if not isinstance(key, ast.keyword): msg = "keyword error in function call '%s'" % (func) self.raise_exception(node, msg=msg) - keywords[key.arg] = self.run(key.value) + if key.arg is None: + keywords.update(self.run(key.value)) + elif key.arg in keywords: + self.raise_exception(node, + msg="keyword argument repeated: %s" % key.arg, + exc=SyntaxError) + else: + keywords[key.arg] = self.run(key.value) kwargs = getattr(node, 'kwargs', None) + if kwargs is not None: keywords.update(self.run(kwargs)) @@ -771,7 +764,8 @@ raise Warning("decorated procedures not supported!") kwargs = [] - if not valid_symbol_name(node.name) or node.name in self.readonly_symbols: + if (not valid_symbol_name(node.name) or + node.name in self.readonly_symbols): errmsg = "invalid function name (reserved word?) %s" % node.name self.raise_exception(node, exc=NameError, msg=errmsg) @@ -803,7 +797,7 @@ self.no_deepcopy.remove(node.name) -class Procedure(object): +class Procedure: """Procedure: user-defined function for asteval. This stores the parsed ast nodes as from the 'functiondef' ast node diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/asteval/astutils.py new/asteval-0.9.22/asteval/astutils.py --- old/asteval-0.9.18/asteval/astutils.py 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/asteval/astutils.py 2021-02-11 22:05:29.000000000 +0100 @@ -4,15 +4,15 @@ Matthew Newville <[email protected]>, The University of Chicago """ -import io -import re import ast +import io import math import numbers +import re from sys import exc_info -from tokenize import (tokenize as generate_tokens, - ENCODING as tk_ENCODING, - NAME as tk_NAME) +from tokenize import ENCODING as tk_ENCODING +from tokenize import NAME as tk_NAME +from tokenize import tokenize as generate_tokens HAS_NUMPY = False numpy = None @@ -20,6 +20,7 @@ import numpy ndarr = numpy.ndarray HAS_NUMPY = True + numpy_version = numpy.version.version.split('.', 2) except ImportError: pass @@ -159,14 +160,15 @@ 'arctanh', 'acosh': 'arccosh', 'asinh': 'arcsinh'} -def _open(filename, mode='r', buffering=0): +def _open(filename, mode='r', buffering=-1): """read only version of open()""" if mode not in ('r', 'rb', 'rU'): raise RuntimeError("Invalid open file mode, must be 'r', 'rb', or 'rU'") if buffering > MAX_OPEN_BUFFER: - raise RuntimeError("Invalid buffering value, max buffer size is {}".format(MAX_OPEN_BUFFER)) + raise RuntimeError(f"Invalid buffering value, max buffer size is {MAX_OPEN_BUFFER}") return open(filename, mode, buffering) + def _type(obj, *varargs, **varkws): """type that prevents varargs and varkws""" return type(obj).__name__ @@ -181,25 +183,25 @@ """safe version of pow""" if isinstance(exp, numbers.Number): if exp > MAX_EXPONENT: - raise RuntimeError("Invalid exponent, max exponent is {}".format(MAX_EXPONENT)) + raise RuntimeError(f"Invalid exponent, max exponent is {MAX_EXPONENT}") elif HAS_NUMPY: if isinstance(exp, numpy.ndarray): if numpy.nanmax(exp) > MAX_EXPONENT: - raise RuntimeError("Invalid exponent, max exponent is {}".format(MAX_EXPONENT)) + raise RuntimeError(f"Invalid exponent, max exponent is {MAX_EXPONENT}") return base ** exp def safe_mult(a, b): """safe version of multiply""" if isinstance(a, str) and isinstance(b, int) and len(a) * b > MAX_STR_LEN: - raise RuntimeError("String length exceeded, max string length is {}".format(MAX_STR_LEN)) + raise RuntimeError(f"String length exceeded, max string length is {MAX_STR_LEN}") return a * b def safe_add(a, b): """safe version of add""" if isinstance(a, str) and isinstance(b, str) and len(a) + len(b) > MAX_STR_LEN: - raise RuntimeError("String length exceeded, max string length is {}".format(MAX_STR_LEN)) + raise RuntimeError(f"String length exceeded, max string length is {MAX_STR_LEN}") return a + b @@ -207,11 +209,11 @@ """safe version of lshift""" if isinstance(b, numbers.Number): if b > MAX_SHIFT: - raise RuntimeError("Invalid left shift, max left shift is {}".format(MAX_SHIFT)) + raise RuntimeError(f"Invalid left shift, max left shift is {MAX_SHIFT}") elif HAS_NUMPY: if isinstance(b, numpy.ndarray): if numpy.nanmax(b) > MAX_SHIFT: - raise RuntimeError("Invalid left shift, max left shift is {}".format(MAX_SHIFT)) + raise RuntimeError(f"Invalid left shift, max left shift is {MAX_SHIFT}") return a << b @@ -291,7 +293,7 @@ ReturnedNone = Empty() -class ExceptionHolder(object): +class ExceptionHolder: """Basic exception handler.""" def __init__(self, node, exc=None, msg='', expr=None, lineno=None): @@ -344,10 +346,12 @@ self.names.append(node.id) ast.NodeVisitor.generic_visit(self, node) + builtins = __builtins__ if not isinstance(builtins, dict): builtins = builtins.__dict__ + def get_ast_names(astnode): """Return symbol Names from an AST node.""" finder = NameFinder() @@ -382,14 +386,20 @@ symtable[sym] = getattr(math, sym) if HAS_NUMPY and use_numpy: + # aliases deprecated in NumPy v1.20.0 + deprecated = ['str', 'bool', 'int', 'float', 'complex', 'pv', 'rate', + 'pmt', 'ppmt', 'npv', 'nper', 'long', 'mirr', 'fv', + 'irr', 'ipmt'] for sym in FROM_NUMPY: + if (int(numpy_version[0]) == 1 and int(numpy_version[1]) >= 20 and + sym in deprecated): + continue if hasattr(numpy, sym): symtable[sym] = getattr(numpy, sym) for name, sym in NUMPY_RENAMES.items(): if hasattr(numpy, sym): symtable[name] = getattr(numpy, sym) - symtable.update(LOCALFUNCS) symtable.update(kws) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/asteval.egg-info/PKG-INFO new/asteval-0.9.22/asteval.egg-info/PKG-INFO --- old/asteval-0.9.18/asteval.egg-info/PKG-INFO 2019-12-17 18:36:52.000000000 +0100 +++ new/asteval-0.9.22/asteval.egg-info/PKG-INFO 2021-02-11 22:05:48.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: asteval -Version: 0.9.18 +Version: 0.9.22 Summary: Safe, minimalistic evaluator of python expression using ast module Home-page: http://github.com/newville/asteval Author: Matthew Newville @@ -22,4 +22,4 @@ Classifier: Intended Audience :: Science/Research Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Requires-Python: >=3.5 +Requires-Python: >=3.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/asteval.egg-info/SOURCES.txt new/asteval-0.9.22/asteval.egg-info/SOURCES.txt --- old/asteval-0.9.18/asteval.egg-info/SOURCES.txt 2019-12-17 18:36:52.000000000 +0100 +++ new/asteval-0.9.22/asteval.egg-info/SOURCES.txt 2021-02-11 22:05:48.000000000 +0100 @@ -1,7 +1,11 @@ +.gitattributes +.gitignore INSTALL LICENSE MANIFEST.in README.rst +azure-pipelines.yml +requirements.txt setup.cfg setup.py versioneer.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/azure-pipelines.yml new/asteval-0.9.22/azure-pipelines.yml --- old/asteval-0.9.18/azure-pipelines.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/asteval-0.9.22/azure-pipelines.yml 2021-02-11 22:05:29.000000000 +0100 @@ -0,0 +1,39 @@ +# Python package +# Create and test a Python package on multiple Python versions. +# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +trigger: +- master + +pool: + vmImage: 'ubuntu-latest' +strategy: + matrix: + Python36: + python.version: '3.6' + Python37: + python.version: '3.7' + Python38: + python.version: '3.8' + Python39: + python.version: '3.9' + +steps: +- task: UsePythonVersion@0 + inputs: + versionSpec: '$(python.version)' + displayName: 'Use Python $(python.version)' + +- script: | + python -m pip install --upgrade pip + pip install numpy codecov coverage pytest pytest-azurepipelines + displayName: 'Install dependencies' + +- script: | + python setup.py install + cd tests + coverage run --source=asteval -m pytest + coverage report -m + bash <(curl -s https://codecov.io/bash) + displayName: 'Install asteval, run test suite and measure coverage' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/doc/_templates/indexsidebar.html new/asteval-0.9.22/doc/_templates/indexsidebar.html --- old/asteval-0.9.18/doc/_templates/indexsidebar.html 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/doc/_templates/indexsidebar.html 2021-02-11 22:05:29.000000000 +0100 @@ -9,6 +9,3 @@ Development version: <br> <a href="https://github.com/newville/asteval/">github.com</a> <hr> - - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/doc/api.rst new/asteval-0.9.22/doc/api.rst --- old/asteval-0.9.18/doc/api.rst 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/doc/api.rst 2021-02-11 22:05:29.000000000 +0100 @@ -8,7 +8,6 @@ .. module:: asteval - The asteval module has a pretty simple interface, providing an :class:`Interpreter` class which creates an Interpreter of expressions and code. There are a few options available to control what language features diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/doc/conf.py new/asteval-0.9.22/doc/conf.py --- old/asteval-0.9.18/doc/conf.py 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/doc/conf.py 2021-02-11 22:05:29.000000000 +0100 @@ -11,6 +11,7 @@ # serve to show the default. import sys, os +from datetime import date # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -27,7 +28,7 @@ 'sphinx.ext.coverage', 'sphinx.ext.mathjax', 'sphinx.ext.intersphinx'] -intersphinx_mapping = {'py': ('http://docs.python.org/', None)} +intersphinx_mapping = {'py': ('https://docs.python.org/3/', None)} # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -43,7 +44,7 @@ # General information about the project. project = u'asteval' -copyright = u'2014, Matthew Newville, The University of Chicago' +copyright = u'{}, Matthew Newville, The University of Chicago'.format(date.today().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -51,7 +52,7 @@ # # The short X.Y version. import asteval -release = asteval.__version__ +release = asteval.__version__.split('+', 1)[0] # The language for content autogenerated by Sphinx. Refer to documentation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/doc/index.rst new/asteval-0.9.22/doc/index.rst --- old/asteval-0.9.18/doc/index.rst 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/doc/index.rst 2021-02-11 22:05:29.000000000 +0100 @@ -5,52 +5,54 @@ .. _numpy: http://docs.scipy.org/doc/numpy -The asteval package evaluates mathematical expressions and statements, -providing a safer alternative to Python's builtin :py:func:`eval` and a -richer, easier to use alternative to :py:func:`ast.literal_eval`. It does -this by building an embedded interpreter for a subset of the Python -language using Python's :py:mod:`ast` module. The emphasis here and main -area of application is the evaluation of mathematical expressions. Because -of this, mathematical functions from Python's :py:mod:`math` module are -available, and a large number of functions from `numpy`_ will be available -if `numpy`_ is installed on your system. - -While the emphasis is on basic mathematical expressions, many features of the -Python language are supported by default. These features include array -slicing and subscripting, if-then-else conditionals, while loops, for loops, -try-except blocks, list comprehension, and user-defined functions. All -objects in the asteval interpreter are truly python objects, and all basic -built-in data structures (strings, dictionaries, tuple, lists, numpy arrays) -are supported. +The asteval package evaluates Python expressions and statements, providing +a safer alternative to Python's builtin :py:func:`eval` and a richer, +easier to use alternative to :py:func:`ast.literal_eval`. It does this by +building an embedded interpreter for a subset of the Python language using +Python's :py:mod:`ast` module. The emphasis and main area of application +is the evaluation of mathematical expressions. Because of this emphasis, +mathematical functions from Python's :py:mod:`math` module are built-in to +asteval, and a large number of functions from `numpy`_ will be available if +`numpy`_ is installed on your system. + +While the primary goal is evaluation of mathematical expressions, many +features and constructs of the Python language are supported by default. +These features include array slicing and subscripting, if-then-else +conditionals, while loops, for loops, try-except blocks, list +comprehension, and user-defined functions. All objects in the asteval +interpreter are truly Python objects, and all of the basic built-in data +structures (strings, dictionaries, tuple, lists, sets, numpy arrays) are +supported, including the built-in methods for these objects. -However, Asteval is by no means an attempt to reproduce Python with its own +However, asteval is by no means an attempt to reproduce Python with its own :py:mod:`ast` module. There are important differences and missing features -compared to Python. Many of these absences are intentionally trying to make a -safer version of :py:func:`eval`, while some are simply due to the reduced -requirements for a small embedded min-language. Some of the main differences -and absences include: +compared to Python. Many of these absences are intentional, and part of +the effort to try to make a safer version of :py:func:`eval`, while some +are simply due to the reduced requirements for an embedded mini-language. +These differences and absences include: 1. Variable and function symbol names are held in a simple symbol table - a single dictionary - giving a flat namespace. 2. creating classes is not allowed. 3. importing modules is not allowed. - 4. function decorators, generators, yield, and lambda are not supported. - 5. Many builtins (:py:func:`eval`, :py:func:`execfile`, + 4. f-strings, function decorators, generators, yield, type hint, and + `lambda` are not supported. + 5. Many builtin functions (:py:func:`eval`, :py:func:`execfile`, :py:func:`getattr`, :py:func:`hasattr`, :py:func:`setattr`, and :py:func:`delattr`) are not allowed. 6. Accessing several private object attributes that can provide access to the python interpreter are not allowed. -The effect to make the asteval mini-language look and act very much like -miniature version of Python itself focused on mathematical calculations, and -with noticeable limitations. - - -Because asteval is suitable for evaluating user-supplied input strings, safety -against malicious or incompetent user input is an important concern. Asteval -tries as hard as possible to prevent user-supplied input from crashing the -Python interpreter or from returning exploitable parts of the Python -interpreter. In this sense asteval is certainly safer than using +The resulting "asteval language" acts very much like miniature version of +Python, focused on mathematical calculations, and with noticeable +limitations. It is the kind of toy programming language you might use to +introduce simple scientific programming concepts. + +Because asteval is designed for evaluating user-supplied input, safety +against malicious or incompetent user input is an important concern. +Asteval tries as hard as possible to prevent user-supplied input from +crashing the Python interpreter or from returning exploitable parts of the +Python interpreter. In this sense asteval is certainly safer than using :py:func:`eval`. However, asteval is an open source project written by volunteers, and we cannot guarantee that it is completely safe against malicious attacks. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/doc/installation.rst new/asteval-0.9.22/doc/installation.rst --- old/asteval-0.9.18/doc/installation.rst 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/doc/installation.rst 2021-02-11 22:05:29.000000000 +0100 @@ -10,9 +10,18 @@ ~~~~~~~~~~~~~~~ Asteval is a pure python module with no required dependencies outside of the -standard library. Asteval will make use of the `numpy`_ module if available. +standard library. Asteval will make use of the `numpy`_ module if +available. The test suite requires the `pytest` module. -Version 0.9.18 supports and is tested with Python 3.5 through 3.8. +The latest stable version of asteval is |release|. + + +Versions 0.9.21 and 0.9.22 support and are tested with Python 3.6 through +3.9. Python 3.6 will be supported until at least its official end of life +(December 2021). No released version supports Python 3.10 yet. + +Versions 0.9.18, 0.9.19, and 0.9.20 supported and were tested with Python +3.5 through 3.8. Version 0.9.17 was the last version to support Python 2.7. @@ -20,14 +29,16 @@ Download and Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The latest stable version of asteval is |release| and is available at `PyPI`_ or as -a conda package. You should be able to install asteval with:: +The latest stable version of asteval is |release| and is available at +`PyPI`_ or as a conda package. You should be able to install asteval +with:: pip install asteval -or:: - - conda install -c GSECARS asteval +It may also be available on some conda channels, including `conda-forge`, +but as it is a pure Python package with no dependencies or OS-specific +extensions, using `pip` should be the preferred method on all platforms and +environments. Development Version ~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/doc/motivation.rst new/asteval-0.9.22/doc/motivation.rst --- old/asteval-0.9.18/doc/motivation.rst 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/doc/motivation.rst 2021-02-11 22:05:29.000000000 +0100 @@ -12,41 +12,41 @@ A completely fair question is: Why is this desirable? That is, why not simply use :py:func:`eval`, or just use Python itself? -The short answer is that sometimes you want to allow evaluation of user input, -or expose a simple (or even "scientific") calculator inside a larger -application. For this, :py:func:`eval` is pretty scary, as it exposes *all* -of Python, which makes user input difficult to trust. Since asteval does not -support the **import** statement or many other constructs, user code cannot -access the :py:mod:`os` and :py:mod:`sys` modules or any functions or classes -outside those provided in the symbol table. +The short answer is that sometimes you want to allow evaluation of user +input, or expose a simple or even scientific calculator inside a larger +application. For this, :py:func:`eval` is pretty scary, as it exposes +*all* of Python, which makes user input difficult to trust. Since asteval +does not support the **import** statement or many other constructs, user +code cannot access the :py:mod:`os` and :py:mod:`sys` modules or any +functions or classes outside those provided in the symbol table. Many of the other missing features (modules, classes, lambda, yield, generators) are similarly motivated by a desire for a safer version of :py:func:`eval`. The idea for asteval is to make a simple procedural, -mathematically oriented language that can be embedded into larger +mathematically-oriented language that can be embedded into larger applications. In fact, the asteval module grew out the the need for a simple expression evaluator for scientific applications such as the `lmfit`_ and `xraylarch`_ modules. An early attempt using the pyparsing module worked but was -error-prone and difficult to maintain. While the simplest of calculators or -expressiona-evaluators is not hard with pyparsing, it turned out that using -the Python :py:mod:`ast` module makes for a much easier and feature rich -simple calculator scientific calculator, including slicing, complex numbers, -keyword arguments to functions, etc. In fact, it is so easy that adding more -complex programming constructs like conditionals, loops, exception handling, -and even user-defined functions was fairly simple to implement. Importantly, -because parsing is done by the :py:mod:`ast` module, whole classes of -implementation errors disappear. Valid python expression will be parsed -correctly and converted into an Abstract Syntax Tree. Furthermore, the -resulting AST is easy to walk through, greatly simplifying evaluation over any -other approach. What started as a desire for a simple expression evaluator -grew into a quite useable procedural domain-specific language for mathematical -applications. - -Asteval makes no claims about speed. Evaluating the AST involves many function -calls, which is going to be slower than Python. In preliminary tests, it's -about 4x slower than Python. That said, for certain use cases (see +error-prone and difficult to maintain. While the simplest of calculators +or expressiona-evaluators is not hard with pyparsing, it turned out that +using the Python :py:mod:`ast` module makes it much easier to implement a +feature-rich scientific calculator, including slicing, complex numbers, +keyword arguments to functions, etc. In fact, this approach meant that +adding more complex programming constructs like conditionals, loops, +exception handling, and even user-defined functions was fairly simple. An +important benefit of using the :py:mod:`ast` module is that whole +categories of implementation errors involving parsing, lexing, and defining a +grammar disappear. Any valid python expression will be parsed correctly +and converted into an Abstract Syntax Tree. Furthermore, the resulting AST +is easy to walk through, greatly simplifying the evaluation process. What +started as a desire for a simple expression evaluator grew into a quite +useable procedural domain-specific language for mathematical applications. + +Asteval makes no claims about speed. Evaluating the AST involves many +function calls, which is going to be slower than Python - often 4x slower +than Python. That said, for certain use cases (see https://stackoverflow.com/questions/34106484), use of asteval and numpy can approach the speed of `eval` and the `numexpr` modules. @@ -105,12 +105,12 @@ can take a noticeable amount of CPU time. It is not hard to come up with short program that would run for hundreds of years, which probably exceeds anyones threshold for an acceptable run-time. But there simply is not an -obvious way to predict how long any code will take to run from the text of the -code itself. As a simple example, consider the expression `x**y**z`. For -values of `x`, `y`, and `z`. For `x=y=z=5`, runtime will be well under 0.001 -seconds. For `x=y=z=8`, runtime will still be under 1 sec. For `x=8, y=9, -z=9`, runtime will several seconds. But for `x=y=z=9`, runtime may exceed 1 -hour on some machines. In short, runtime cannot be determined lexically. +obvious way to predict how long any code will take to run from the text of +the code itself. As a simple example, consider the expression `x**y**z`. +For values `x=y=z=5`, runtime will be well under 0.001 seconds. For +`x=y=z=8`, runtime will still be under 1 sec. For `x=8, y=9, z=9`, runtime +will several seconds. But for `x=y=z=9`, runtime may exceed 1 hour on some +machines. In short, runtime cannot be determined lexically. This example also demonstrates there is not a good way to check for a long-running calculation within a single Python process. That calculation is @@ -146,7 +146,7 @@ functions will also be supported. This doesn't really elevate permissions, but it does allow the user of the `asteval` interpreter to read files with the privileges of the calling program. In some cases, this may not be -desireable, and you may want to remove some of these functions from the +desirable, and you may want to remove some of these functions from the symbol table, re-implement them, or ensure that your program cannot access information on disk that should be kept private. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/requirements.txt new/asteval-0.9.22/requirements.txt --- old/asteval-0.9.18/requirements.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/asteval-0.9.22/requirements.txt 2021-02-11 22:05:29.000000000 +0100 @@ -0,0 +1,5 @@ +## +## pip requirements file. To install dependencies, use +## +## pip install -r requirements.txt +## numpy>=1.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/setup.py new/asteval-0.9.22/setup.py --- old/asteval-0.9.18/setup.py 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/setup.py 2021-02-11 22:05:29.000000000 +0100 @@ -1,5 +1,6 @@ #!/usr/bin/env python from setuptools import setup + import versioneer long_description = """ASTEVAL provides a numpy-aware, safe(ish) 'eval' function @@ -12,26 +13,22 @@ Expressions can be compiled into ast node for later evaluation, using the values in the symbol table current at evaluation time. """ -install_reqs = [] -test_reqs = ['pytest'] - setup(name='asteval', version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), author='Matthew Newville', author_email='[email protected]', url='http://github.com/newville/asteval', - license = 'OSI Approved :: MIT License', - python_requires='>=3.5', + license='OSI Approved :: MIT License', + python_requires='>=3.6', description="Safe, minimalistic evaluator of python expression using ast module", long_description=long_description, packages=['asteval'], - install_requires=install_reqs, - tests_require=test_reqs, + tests_require=['pytest'], classifiers=['Intended Audience :: End Users/Desktop', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'Operating System :: OS Independent', 'Programming Language :: Python', - ], + ], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-0.9.18/tests/test_asteval.py new/asteval-0.9.22/tests/test_asteval.py --- old/asteval-0.9.18/tests/test_asteval.py 2019-12-17 18:36:32.000000000 +0100 +++ new/asteval-0.9.22/tests/test_asteval.py 2021-02-11 22:05:29.000000000 +0100 @@ -8,16 +8,14 @@ import textwrap import time import unittest -import pytest -from io import StringIO - from functools import partial +from io import StringIO from sys import version_info from tempfile import NamedTemporaryFile -from asteval import NameFinder, Interpreter, make_symbol_table - +import pytest +from asteval import Interpreter, NameFinder, make_symbol_table HAS_NUMPY = False try: @@ -28,6 +26,9 @@ HAS_NUMPY = False +version_info = (version_info.major, version_info.minor) + + class TestCase(unittest.TestCase): """testing of asteval""" def setUp(self): @@ -95,7 +96,6 @@ if HAS_NUMPY: assert_allclose(tval, val, rtol=1.e-4, atol=1.e-4) - # noinspection PyUnresolvedReferences def istrue(self, expr): """assert that an expression evaluates to True""" @@ -141,6 +141,20 @@ self.istrue("a_dict['a'] == 1") self.istrue("a_dict['d'] == 4") + def test_dict_set_index(self): + """dictionary indexing""" + self.interp("a_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}") + self.interp("a_dict['a'] = -4") + self.interp("a_dict['e'] = 73") + + self.istrue("a_dict['a'] == -4") + self.istrue("a_dict['e'] == 73") + + self.interp("b_dict = {}") + self.interp("keyname = 'a'") + self.interp("b_dict[keyname] = (1, -1, 'x')") + self.istrue("b_dict[keyname] == (1, -1, 'x')") + def test_list_index(self): """list indexing""" self.interp("a_list = ['a', 'b', 'c', 'd', 'o']") @@ -166,8 +180,8 @@ """nd array indexing""" if HAS_NUMPY: self.interp("a_ndarray = 5*arange(20)") - assert(self.interp("a_ndarray[2]") == 10) - assert(self.interp("a_ndarray[4]") == 20) + assert(self.interp("a_ndarray[2]") == 10) + assert(self.interp("a_ndarray[4]") == 20) def test_ndarrayslice(self): """array slicing""" @@ -177,6 +191,7 @@ self.interp("y = arange(20).reshape(4, 5)") self.istrue("y[:,3] == array([3, 8, 13, 18])") self.istrue("y[...,1] == array([1, 6, 11, 16])") + self.istrue("y[1,:] == array([5, 6, 7, 8, 9])") self.interp("y[...,1] = array([2, 2, 2, 2])") self.istrue("y[1,:] == array([5, 2, 7, 8, 9])") @@ -265,6 +280,8 @@ self.check_error(None) self.interp('assert n==7') self.check_error('AssertionError') + self.interp('assert n==7, "no match"') + self.check_error('AssertionError', 'no match') def test_for(self): """for loops""" @@ -731,7 +748,7 @@ """test function with kw args, no **kws""" self.interp(textwrap.dedent(""" def fcn(x=0, y=0, z=0, t=0, square=False): - 'test varargs function' + 'test kwargs function' out = 0 for i in (x, y, z, t): if square: @@ -804,8 +821,25 @@ self.interp("o = fcn(1, x=2)") self.check_error('TypeError') + def test_kwargx(self): + """test passing and chaining in **kwargs""" + self.interp(textwrap.dedent(""" + def inner(foo=None, bar=None): + return (foo, bar) + + def outer(**kwargs): + return inner(**kwargs) + """)) + + ret = self.interp("inner(foo='a', bar=2)") + assert(ret == ('a', 2)) + ret = self.interp("outer(foo='a', bar=7)") + assert(ret == ('a', 7)) + ret = self.interp("outer(**dict(foo='b', bar=3))") + assert(ret == ('b', 3)) + def test_nested_functions(self): - setup=""" + setup = """ def a(x=10): if x > 5: return 1 @@ -825,7 +859,6 @@ self.isvalue('o1', 3.5) self.isvalue('o2', 1.5) - def test_astdump(self): """test ast parsing and dumping""" astnode = self.interp.parse('x = 1') @@ -855,7 +888,6 @@ self.interp("1<<1001") self.check_error('RuntimeError') - def test_safe_open(self): self.interp('open("foo1", "wb")') self.check_error('RuntimeError') @@ -882,8 +914,12 @@ self.interp("9**9**9**9**9**9**9**9") self.check_error('RuntimeError') # Safe, safe_pow() catches this self.interp( - "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))") - self.check_error('MemoryError') # Hmmm, this is caught, but its still concerning... + "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... self.interp("compile('xxx')") self.check_error('NameError') # Safe, compile() is not supported @@ -944,9 +980,9 @@ x2 = aeval.symtable['x2'] x3 = aeval.symtable['x3'] - assert_allclose(x1, 0.50, rtol=0.001) + assert_allclose(x1, 0.50, rtol=0.001) assert_allclose(x2, 0.866025, rtol=0.001) - assert_allclose(x3, 1.00, rtol=0.001) + assert_allclose(x3, 1.00, rtol=0.001) def test_readonly_symbols(self): @@ -984,7 +1020,6 @@ assert(aeval("x") == 21) assert(aeval("y") == 17) - assert(aeval("abs(8)") == 8) assert(aeval("abs(-8)") == 8) aeval("def abs(x): return x*2") @@ -999,7 +1034,6 @@ assert(aeval2("abs(8)") == 8) assert(aeval2("abs(-8)") == 8) - def test_chained_compparisons(self): self.interp('a = 7') self.interp('b = 12') @@ -1036,10 +1070,33 @@ # uses the function's __name__ attribute. Partials don't have a # __name__ attribute, so we want to make sure that an AttributeError is # not raised. - + result = aeval("sqrt(-1)") assert aeval.error.pop().exc == ValueError + def test_inner_return(self): + self.interp(textwrap.dedent(""" + def func(): + loop_cnt = 0 + for i in range(5): + for k in range(5): + loop_cnt += 1 + return (i, k, loop_cnt) + """)) + out = self.interp("func()") + assert out == (0, 4, 5) + + def test_nested_break(self): + self.interp(textwrap.dedent(""" + def func_w(): + for k in range(5): + if k == 4: + break + k = 100 + return k + """)) + assert 4 == self.interp("func_w()") + class TestCase2(unittest.TestCase): def test_stringio(self): @@ -1050,5 +1107,6 @@ intrep("print('out')") self.assertEqual(out.getvalue(), 'out\n') + if __name__ == '__main__': pytest.main(['-v', '-x', '-s'])
