Repository navigation
Ideas for making ast.literal_eval() usable #83340
Description
Activity
A primary goal for ast.literal_eval() is to "Safely evaluate an expression node or a string".
In the context of a real application, we need to do several things to make it possible to fulfill its design goal:
-
We should document possible exceptions that need to be caught. So far, I've found TypeError, MemoryError, SyntaxError, ValueError.
-
Define a size limit guaranteed not to give a MemoryError. The smallest unsafe size I've found so far is 301 characters:
s = '(' * 100 + '0' + ',)' * 100
literal_eval(s) # Raises a MemoryError -
Consider writing a standalone expression compiler that doesn't have the same small limits as our usual compile() function. This would make literal_eval() usable for evaluating tainted inputs with bigger datasets. (Imagine if the json module could only be safely used with inputs under 301 characters).
-
Perhaps document an example of how we suggest that someone process tainted input:
expr = input('Enter a dataset in Python format: ') if len(expr) > 300: error(f'Maximum supported size is 300, not {len(expr)}') try: data = literal_eval(expr) except (TypeError, MemoryError, SyntaxError, ValueError): error('Input cannot be evaluated')
-
- We should document possible exceptions that need to be caught. So far, I've found TypeError, MemoryError, SyntaxError, ValueError.
Maybe we should wrap all of these into something like LiteralEvalError to easily catch all of them, LiteralEvalError can be subclass of that four but I guess in some cases this change might break code.
- Define a size limit guaranteed not to give a MemoryError. The smallest unsafe size I've found so far is 301 characters:
>>> s = "(" * 101 + ")" * 101 >>> len(s) 202 >>> ast.literal_eval(s) s_push: parser stack overflow Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.9/ast.py", line 61, in literal_eval node_or_string = parse(node_or_string, mode='eval') File "/usr/local/lib/python3.9/ast.py", line 49, in parse return compile(source, filename, mode, flags, MemoryError
- Consider writing a standalone expression compiler that doesn't have the same small limits as our usual compile() function. This would make literal_eval() usable for evaluating tainted inputs with bigger datasets. (Imagine if the json module could only be safely used with inputs under 301 characters).
Can you explain it a bit more detailed, how does this standalone expression compiler should work?
- addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory3.9 (EOL)end of lifeend of lifetype-featureA feature request or enhancementA feature request or enhancement
on Feb 14, 2020 - We should document possible exceptions that need to be caught. So far, I've found TypeError, MemoryError, SyntaxError, ValueError.
Also, an addition to these errors is RecursionError >>> t = ast.Tuple(elts=[], ctx=ast.Load()) >>> t.elts.append(t) >>> ast.literal_eval(t) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.9/ast.py", line 101, in literal_eval return _convert(node_or_string) File "/usr/local/lib/python3.9/ast.py", line 81, in _convert return tuple(map(_convert, node.elts)) File "/usr/local/lib/python3.9/ast.py", line 81, in _convert return tuple(map(_convert, node.elts)) File "/usr/local/lib/python3.9/ast.py", line 81, in _convert return tuple(map(_convert, node.elts)) [Previous line repeated 496 more times] RecursionError: maximum recursion depth exceeded
It can also crash.
ast.literal_eval('+0'*10**6)
The cause is that all AST handling C code (in particularly converting the AST from C to Python) is recursive, and therefore can overflow the C stack. Some recursive code has arbitrary limits which cause raising exceptions like MemoryError in the initial example, but not all code has such checks.
Reacted by Gregory P. SmithCan you explain it a bit more detailed,
how does this standalone expression compiler should work?Aim for something like JSON parser but for the supported Python constant expressions and with the existing tokenize module to feed a hand-rolled on-recursive parser
related: the old #55552 issue about producing better errors from the parser. not crashing due to a C stack overflow counts as better. ;)
- added3.12only security fixesonly security fixesand removed3.9 (EOL)end of lifeend of life
on Aug 12, 2022
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields: