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 2025-11-14 16:13:53 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-asteval (Old) and /work/SRC/openSUSE:Factory/.python-asteval.new.2061 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-asteval" Fri Nov 14 16:13:53 2025 rev:22 rq:1317640 version:1.0.7 Changes: -------- --- /work/SRC/openSUSE:Factory/python-asteval/python-asteval.changes 2025-01-27 20:58:56.755763625 +0100 +++ /work/SRC/openSUSE:Factory/.python-asteval.new.2061/python-asteval.changes 2025-11-14 16:14:08.815382415 +0100 @@ -1,0 +2,17 @@ +Thu Nov 13 14:25:13 UTC 2025 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 1.0.7 + * return in while, for, and with blocks in functions properly signal + an immediate exit of the function. (fixes #141) + * fix augassign to retain line numbers for exceptions properly. (#140) + * make sure that "unsafe_modules" should not be accessed even if imported + and exposed by other modules + * cleanup unused nodes + * tweaks and fixes to docs + * drop testing with Python 3.9 + * add testing for Python 3.14 + * update pyproject.toml from the endless churn from PyPA, to move the + location and formatting for the LICENSE, to continue compliance with + the endless churn from PyPA. + +------------------------------------------------------------------- Old: ---- asteval-1.0.6.tar.gz New: ---- asteval-1.0.7.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-asteval.spec ++++++ --- /var/tmp/diff_new_pack.iWuQ4X/_old 2025-11-14 16:14:13.715588088 +0100 +++ /var/tmp/diff_new_pack.iWuQ4X/_new 2025-11-14 16:14:13.715588088 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-asteval -Version: 1.0.6 +Version: 1.0.7 Release: 0 Summary: Safe, minimalistic evaluator of python expression using ast module License: MIT ++++++ asteval-1.0.6.tar.gz -> asteval-1.0.7.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/.github/workflows/macos_numpy.yml new/asteval-1.0.7/.github/workflows/macos_numpy.yml --- old/asteval-1.0.6/.github/workflows/macos_numpy.yml 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/.github/workflows/macos_numpy.yml 2025-11-06 22:41:41.000000000 +0100 @@ -8,7 +8,7 @@ strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/.github/workflows/ubuntu_nonumpy.yml new/asteval-1.0.7/.github/workflows/ubuntu_nonumpy.yml --- old/asteval-1.0.6/.github/workflows/ubuntu_nonumpy.yml 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/.github/workflows/ubuntu_nonumpy.yml 2025-11-06 22:41:41.000000000 +0100 @@ -13,7 +13,7 @@ strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/.github/workflows/ubuntu_numpy.yml new/asteval-1.0.7/.github/workflows/ubuntu_numpy.yml --- old/asteval-1.0.6/.github/workflows/ubuntu_numpy.yml 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/.github/workflows/ubuntu_numpy.yml 2025-11-06 22:41:41.000000000 +0100 @@ -13,7 +13,7 @@ strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] with_financial: [0, 1] steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/.github/workflows/windows_numpy.yml new/asteval-1.0.7/.github/workflows/windows_numpy.yml --- old/asteval-1.0.6/.github/workflows/windows_numpy.yml 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/.github/workflows/windows_numpy.yml 2025-11-06 22:41:41.000000000 +0100 @@ -8,7 +8,7 @@ strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/PKG-INFO new/asteval-1.0.7/PKG-INFO --- old/asteval-1.0.6/PKG-INFO 2025-01-19 22:43:44.722988600 +0100 +++ new/asteval-1.0.7/PKG-INFO 2025-11-06 22:41:52.521916200 +0100 @@ -1,45 +1,23 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: asteval -Version: 1.0.6 +Version: 1.0.7 Summary: Safe, minimalistic evaluator of python expression using ast module Author-email: Matthew Newville <[email protected]> -License: The MIT License - - Copyright (c) 2025 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 - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - +License-Expression: MIT Project-URL: Homepage, https://github.com/lmfit/asteval Project-URL: Documentation, https://lmfit.github.io/asteval/ Project-URL: Tracker, https://github.com/lmfit/asteval/issues Keywords: AST,expression evaluation,eval Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=3.9 +Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE Provides-Extra: dev @@ -53,6 +31,7 @@ Requires-Dist: coverage; extra == "test" Provides-Extra: all Requires-Dist: asteval[dev,doc,test]; extra == "all" +Dynamic: license-file ASTEVAL ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/asteval/asteval.py new/asteval-1.0.7/asteval/asteval.py --- old/asteval-1.0.6/asteval/asteval.py 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/asteval/asteval.py 2025-11-06 22:41:41.000000000 +0100 @@ -48,15 +48,16 @@ ExceptionHolder, ReturnedNone, Empty, make_symbol_table, numpy, op2func, safe_getattr, safe_format, valid_symbol_name, Procedure) -ALL_NODES = ['arg', 'assert', 'assign', 'attribute', 'augassign', 'binop', - 'boolop', 'break', 'bytes', 'call', 'compare', 'constant', - 'continue', 'delete', 'dict', 'dictcomp', 'ellipsis', - 'excepthandler', 'expr', 'extslice', 'for', 'functiondef', 'if', - 'ifexp', 'import', 'importfrom', 'index', 'interrupt', 'list', - 'listcomp', 'module', 'name', 'nameconstant', 'num', 'pass', - 'raise', 'repr', 'return', 'set', 'setcomp', 'slice', 'str', - 'subscript', 'try', 'tuple', 'unaryop', 'while', 'with', - 'formattedvalue', 'joinedstr'] +ALL_NODES = ['arg', 'assert', 'assign', 'attribute', 'augassign', + 'binop', 'boolop', 'break', 'call', 'compare', + 'constant', 'continue', 'delete', 'dict', 'dictcomp', + 'excepthandler', 'expr', 'extslice', 'for', + 'functiondef', 'if', 'ifexp', 'import', 'importfrom', + 'index', 'interrupt', 'list', 'listcomp', 'module', + 'name', 'pass', 'raise', 'repr', 'return', 'set', + 'setcomp', 'slice', 'subscript', 'try', 'tuple', + 'unaryop', 'while', 'with', 'formattedvalue', + 'joinedstr'] MINIMAL_CONFIG = {'import': False, 'importfrom': False} @@ -95,7 +96,7 @@ minimal : bool create a minimal interpreter: disable many nodes (see Note 1). config : dict - dictionay listing which nodes to support (see note 2)) + dictionary listing which nodes to support (see note 2)) Notes ----- @@ -152,7 +153,6 @@ self.lineno = 0 self.code_text = [] self.start_time = time.time() - self.node_handlers = {} for node in ALL_NODES: handler = self.unimplemented @@ -160,6 +160,8 @@ handler = getattr(self, f"on_{node}", self.unimplemented) self.node_handlers[node] = handler + self.allow_unsafe_modules = self.config.get('import', False) + # to rationalize try/except try/finally if 'try' in self.node_handlers: self.node_handlers['tryexcept'] = self.node_handlers['try'] @@ -186,6 +188,8 @@ out = None if node in self.node_handlers: out = self.node_handlers.pop(node) + if node == 'import': + self.allow_unsafe_modules = False return out def set_nodehandler(self, node, handler=None): @@ -193,6 +197,8 @@ if handler is None: handler = getattr(self, f"on_{node}", self.unimplemented) self.node_handlers[node] = handler + if node == 'import': + self.allow_unsafe_modules = True return handler def user_defined_symbols(self): @@ -446,6 +452,7 @@ self.retval = self.run(node.value) if self.retval is None: self.retval = ReturnedNone + self._interrupt = node def on_repr(self, node): """Repr.""" @@ -582,8 +589,8 @@ sym = self.run(node.value) if ctx == ast.Del: return delattr(sym, node.attr) - - return safe_getattr(sym, node.attr, self.raise_exception, node) + return safe_getattr(sym, node.attr, self.raise_exception, node, + allow_unsafe_modules=self.allow_unsafe_modules) def on_assign(self, node): # ('targets', 'value') @@ -594,10 +601,18 @@ def on_augassign(self, node): # ('target', 'op', 'value') """Augmented assign.""" + line_info = { + 'lineno': node.lineno, + 'col_offset': node.col_offset, + 'end_lineno': node.end_lineno, + 'end_col_offset': node.end_col_offset + } return self.on_assign(ast.Assign(targets=[node.target], value=ast.BinOp(left=node.target, op=node.op, - right=node.value))) + right=node.value, + **line_info), + **line_info)) def on_slice(self, node): # ():('lower', 'upper', 'step') """Simple slice.""" @@ -721,7 +736,7 @@ self.run(tnode) if self._interrupt is not None: break - if isinstance(self._interrupt, ast.Break): + if isinstance(self._interrupt, (ast.Break, ast.Return)): break else: for tnode in node.orelse: @@ -737,7 +752,7 @@ self.run(tnode) if self._interrupt is not None: break - if isinstance(self._interrupt, ast.Break): + if isinstance(self._interrupt, (ast.Break, ast.Return)): break else: for tnode in node.orelse: @@ -761,11 +776,11 @@ self.run(bnode) if self._interrupt is not None: break - for ctx in contexts: if hasattr(ctx, '__exit__'): ctx.__exit__() + def _comp_save_syms(self, node): """find and save symbols that will be used in a comprehension""" saved_syms = {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/asteval/astutils.py new/asteval-1.0.7/asteval/astutils.py --- old/asteval-1.0.6/asteval/astutils.py 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/asteval/astutils.py 2025-11-06 22:41:41.000000000 +0100 @@ -6,10 +6,13 @@ """ import ast import io +import os +import sys +import ctypes import math import numbers import re -from sys import exc_info + from tokenize import ENCODING as tk_ENCODING from tokenize import NAME as tk_NAME from tokenize import tokenize as generate_tokens @@ -73,6 +76,9 @@ # unsafe attributes for particular objects, by type UNSAFE_ATTRS_DTYPES = {str: ('format', 'format_map')} +# unsafe modules that may be exposed in other modules +# but should be prevented from being accessed +UNSAFE_MODULES = (io, os, sys, ctypes) # inherit these from python's __builtins__ FROM_PY = ('ArithmeticError', 'AssertionError', 'AttributeError', @@ -277,7 +283,7 @@ # Safe version of getattr -def safe_getattr(obj, attr, raise_exc, node): +def safe_getattr(obj, attr, raise_exc, node, allow_unsafe_modules=False): """safe version of getattr""" unsafe = (attr in UNSAFE_ATTRS or (attr.startswith('__') and attr.endswith('__'))) @@ -286,6 +292,11 @@ unsafe = (isinstance(obj, dtype) or obj is dtype) and attr in attrlist if unsafe: break + if not unsafe and not allow_unsafe_modules: + for mod in UNSAFE_MODULES: + unsafe = obj is mod or getattr(obj, attr) is mod + if unsafe: + break if unsafe: msg = f"no safe attribute '{attr}' for {repr(obj)}" raise_exc(node, exc=AttributeError, msg=msg) @@ -382,7 +393,7 @@ self.col_offset = node.col_offset except: pass - self.exc_info = exc_info() + self.exc_info = sys.exc_info() if self.exc is None and self.exc_info[0] is not None: self.exc = self.exc_info[0] if self.msg == '' and self.exc_info[1] is not None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/asteval/version.py new/asteval-1.0.7/asteval/version.py --- old/asteval-1.0.6/asteval/version.py 2025-01-19 22:43:44.000000000 +0100 +++ new/asteval-1.0.7/asteval/version.py 2025-11-06 22:41:52.000000000 +0100 @@ -1,16 +1,34 @@ -# file generated by setuptools_scm +# file generated by setuptools-scm # don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Tuple, Union + from typing import Tuple + from typing import Union + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] else: VERSION_TUPLE = object + COMMIT_ID = object version: str __version__: str __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '1.0.7' +__version_tuple__ = version_tuple = (1, 0, 7) -__version__ = version = '1.0.6' -__version_tuple__ = version_tuple = (1, 0, 6) +__commit_id__ = commit_id = 'gc9991033e' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/asteval.egg-info/PKG-INFO new/asteval-1.0.7/asteval.egg-info/PKG-INFO --- old/asteval-1.0.6/asteval.egg-info/PKG-INFO 2025-01-19 22:43:44.000000000 +0100 +++ new/asteval-1.0.7/asteval.egg-info/PKG-INFO 2025-11-06 22:41:52.000000000 +0100 @@ -1,45 +1,23 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: asteval -Version: 1.0.6 +Version: 1.0.7 Summary: Safe, minimalistic evaluator of python expression using ast module Author-email: Matthew Newville <[email protected]> -License: The MIT License - - Copyright (c) 2025 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 - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - +License-Expression: MIT Project-URL: Homepage, https://github.com/lmfit/asteval Project-URL: Documentation, https://lmfit.github.io/asteval/ Project-URL: Tracker, https://github.com/lmfit/asteval/issues Keywords: AST,expression evaluation,eval Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=3.9 +Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE Provides-Extra: dev @@ -53,6 +31,7 @@ Requires-Dist: coverage; extra == "test" Provides-Extra: all Requires-Dist: asteval[dev,doc,test]; extra == "all" +Dynamic: license-file ASTEVAL ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/doc/motivation.rst new/asteval-1.0.7/doc/motivation.rst --- old/asteval-1.0.6/doc/motivation.rst 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/doc/motivation.rst 2025-11-06 22:41:41.000000000 +0100 @@ -40,7 +40,7 @@ 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 +grew into a quite usable procedural domain-specific language for mathematical applications. Asteval makes no claims about speed. Evaluating the AST involves many @@ -70,6 +70,8 @@ evaluate user-supplied expressions, including `numexpr` and `sympy` use the builtin :py:func:`eval` as part of their processing. +Prohibited Python statements and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some of the things not allowed in the asteval interpreter for safety reasons include: @@ -107,21 +109,26 @@ consider disabling the use of numpy, or take extra care to specify what numpy functions can be used. -In 2024, an independent security audit of asteval done by Andrew Effenhauser, -Ayman Hammad, and Daniel Crowley in the X-Force Security Research division of -IBM showed insecurities with ``string.format``, so that access to this and -``string.format_map`` method were removed. In addition, this audit showed -that the ``numpy`` submodules ``linalg``, ``fft``, and ``polynomial`` expose -many exploitable objects, so these submodules were removed by default. If -needed, these modules can be added to any Interpreter either using the -``user_symbols`` argument when creating it, or adding the needed symbols to the -symbol table after the Interpreter is created. +In 2024, an independent security audit of asteval done by Andrew +Effenhauser, Ayman Hammad, and Daniel Crowley in the X-Force Security +Research division of IBM showed insecurities with ``string.format``, +so that access to this and ``string.format_map`` method were removed. +In addition, this audit showed that the ``numpy`` submodules +``linalg``, ``fft``, and ``polynomial`` expose many exploitable +objects, so these submodules were removed by default. If needed, +these modules can be added to any Interpreter either using the +``user_symbols`` argument when creating it, or adding the needed +symbols to the symbol table after the Interpreter is created. In 2025, William Khem Marquez demonstrated two vulnerabilities: one from leaving some AST objects exposed within the interpreter for user-defined functions ("Procedures"), and one with f-string formatting. Both of these were fixed for version 1.0.6. + +Avoiding resource hogging +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + There are other categories of safety that asteval may attempt to address, but cannot guarantee success. The most important of these is resource hogging, which might be used for a denial-of-service attack. @@ -140,23 +147,24 @@ that value of ``nmax`` almost certainly will, and can even crash the Python shell. -As another example, and an illustration of the fundamental problem, -consider the Python expression ``a = x**y**z``. For values -``x=y=z=5``, the run time will be well under 0.001 seconds. For -``x=y=z=8``, run time will still be under 1 sec. Changing to ``x=8, -y=9, z=9``, Python will ake several seconds (the value is :math:`\sim -10^{350,000,000}`) With ``x=y=z=9``, executing that statement may take -more than 1 hour on some machines. It is not hard to come up with +As another illustration of the fundamental challenge, consider the +Python expression ``a = x**y**z``. With values ``x=y=z=5``, the run +time will be well under 0.001 seconds. Even with ``x=y=z=8``, run +time will be under 1 sec. Changing to ``x=8, y=9, z=9``, Python will +take several seconds (the value is :math:`\sim 10^{350,000,000}`) With +``x=y=z=9``, executing that statement may take more than 1 hour on +some machines. The result is that it is not hard to come up with short program that would run for hundreds of years, which probably exceeds everyones threshold for an acceptable run-time. The point -here is tha there simply is not a good way to predict how long any -code will take to run from the text of the code itself: run time -cannot be determined lexically. +here is that there simply is not a good way to predict runtime for any +code from the text of the code alone: run time cannot be determined +lexically. To be clear, for the ``x**y**z`` exponentiation example, asteval will raise a runtime error, telling you that an exponent > 10,000 is not allowed. Several other attempts are also made to prevent long-running -operations or memory exhaustion. These checks will prevent: +operations or memory exhaustion. These checks will prevent the +following ways to hog resources: * statements longer than 50,000 bytes. * values of exponents (``p`` in ``x**p``) > 10,000. @@ -165,11 +173,16 @@ * more than 262144 open buffers * opening a file with a mode other than ``'r'``, ``'rb'``, or ``'ru'``. -These checks happen at runtime, not by analyzing the text of the code. -As with the example above using ``numpy.arange``, very large arrays -and lists can be created that might approach memory limits. There are -countless other "clever ways" to have very long run times that cannot -be readily predicted from the text of the code. +These checks happen at runtime, not by analyzing the text of the code, +but the values for these operations. Still, as with the example above +using ``numpy.arange``, very large arrays and lists can be created +that can approach memory limits. There are countless "clever ways" to +have very long run times that cannot be readily predicted from the +text of the code. + + +File access +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, the list of supported functions does include Python's ``open()`` -- in read-only mode -- which will allow disk access to the @@ -183,18 +196,24 @@ re-implement them, or ensure that your program cannot access information on disk that should be kept private. -The exponential example also highlights the issue that there is not a good way -to check for a long-running calculation within a single Python process. That -calculation is not stuck within the Python interpreter, but in C code (no doubt -the ``pow()`` function) called by the Python interpreter itself. That call -will not return from the C library to the Python interpreter or allow other -threads to run until that call is done. That means that from within a single -process, there is not a reliable way to tell asteval (or really, even Python) -when a calculation has taken too long: Denial of Service is hard to detect -before it happens, and even challenging to detect while it is happening. The -only reliable way to limit run time is at the level of the operating system, -with a second process watching the execution time of the asteval process and -either try to interrupt it or kill it. + +Monitoring Runtime and interrupting processes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +The exponential example also highlights the issue that there is not a +good way to check for a long-running calculation within a single +Python process. That calculation is not stuck within the Python +interpreter, but in C code (no doubt the ``pow()`` function) called by +the Python interpreter itself. That call will not return from the C +library to the Python interpreter or allow other threads to run until +that call is done. That means that from within a single process, +there is not a reliable way to tell asteval (or really, even Python) +when a calculation has taken too long: Denial of Service is hard to +detect before it happens, and even challenging to detect while it is +happening. The only reliable way to limit run time is at the level of +the operating system, with a second process watching the execution +time of the asteval process and either try to interrupt it or kill it. For a limited range of problems, you can try to avoid asteval taking too long. For example, you may try to limit the *recursion limit* when diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/pyproject.toml new/asteval-1.0.7/pyproject.toml --- old/asteval-1.0.6/pyproject.toml 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/pyproject.toml 2025-11-06 22:41:41.000000000 +0100 @@ -18,24 +18,24 @@ [project] name = "asteval" dynamic = ["version"] -requires-python = ">= 3.9" +requires-python = ">= 3.10" description = "Safe, minimalistic evaluator of python expression using ast module" readme = "README.rst" authors = [ {name = "Matthew Newville", email = "[email protected]"} ] -license = {file = "LICENSE"} +license = "MIT" +license-files = ["LICENSE"] keywords = ["AST", "expression evaluation", "eval"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: PyPy", ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asteval-1.0.6/tests/test_asteval.py new/asteval-1.0.7/tests/test_asteval.py --- old/asteval-1.0.6/tests/test_asteval.py 2025-01-19 22:38:23.000000000 +0100 +++ new/asteval-1.0.7/tests/test_asteval.py 2025-11-06 22:41:41.000000000 +0100 @@ -89,7 +89,6 @@ if chk_type: assert False - def test_py3(): assert version_info.major > 2 @@ -323,6 +322,39 @@ isvalue(interp, 'n', 7) @pytest.mark.parametrize("nested", [False, True]) +def test_while_return(nested): + interp = make_interpreter(nested_symtable=nested) + interp(textwrap.dedent(""" + def func_while(nmax=10): + n = 0 + while n < nmax: + n += 1 + if n > 6: + return 99 + return n + o1 = func_while(3) + o2 = func_while(12) + """)) + isvalue(interp, 'o1', 3) + isvalue(interp, 'o2', 99) + [email protected]("nested", [False, True]) +def test_for_return(nested): + interp = make_interpreter(nested_symtable=nested) + interp(textwrap.dedent(""" + def func_for(nmax=10): + for n in range(nmax): + if n > 10: + return -2 + return n + o1 = func_for(7) + o2 = func_for(99) + """)) + isvalue(interp, 'o1', 6) + isvalue(interp, 'o2', -2) + + [email protected]("nested", [False, True]) def test_with(nested): "test with" interp = make_interpreter(nested_symtable=nested) @@ -1586,7 +1618,7 @@ etype, fullmsg = error.get_error() assert 'no safe attribute' in error.msg assert etype == 'AttributeError' - + @pytest.mark.parametrize("nested", [False, True]) def test_unsafe_format_string_access(nested): """ @@ -1602,7 +1634,7 @@ etype, fullmsg = error.get_error() assert 'no safe attribute' in error.msg assert etype == 'AttributeError' - + @pytest.mark.parametrize("nested", [False, True]) def test_unsafe_attr_dtypes(nested): """ @@ -1617,12 +1649,12 @@ etype, fullmsg = error.get_error() assert 'no safe attribute' in error.msg assert etype == 'AttributeError' - + interp = make_interpreter(nested_symtable=nested) interp(textwrap.dedent(""" str.format('{0}', dict) """), raise_errors=False) - + error = interp.error[0] etype, fullmsg = error.get_error() assert 'no safe attribute' in error.msg @@ -1645,5 +1677,17 @@ assert 'unsupported operand' in out assert len(interp.error) == 0 [email protected]("nested", [False, True]) +def test_augassign_lineno(nested): + """test lineno on augassign operation""" + interp = make_interpreter(nested_symtable=nested) + + interp(textwrap.dedent(""" + x = 1 + x /= 0 + """)) + check_error(interp, 'ZeroDivisionError', 'x /= 0') + assert interp.error[0].lineno == 3 + if __name__ == '__main__': pytest.main(['-v', '-x', '-s'])
