Skip to content

Commit 5799eef

Browse files
committed
gh-124503: simplified ast.literal_eval
The implementation does not create anymore local functions which reduces the overhead for small inputs. Some other calls are inlined into a single `_convert_literal` function. We have a gain of 10-20% for small inputs and only 1-2% for bigger inputs.
1 parent 46cbdf9 commit 5799eef

File tree

1 file changed

+52
-45
lines changed

1 file changed

+52
-45
lines changed

Lib/ast.py

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -57,53 +57,60 @@ def literal_eval(node_or_string):
5757
Caution: A complex expression can overflow the C stack and cause a crash.
5858
"""
5959
if isinstance(node_or_string, str):
60-
node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval')
61-
if isinstance(node_or_string, Expression):
60+
node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval').body
61+
elif isinstance(node_or_string, Expression):
6262
node_or_string = node_or_string.body
63-
def _raise_malformed_node(node):
64-
msg = "malformed node or string"
65-
if lno := getattr(node, 'lineno', None):
66-
msg += f' on line {lno}'
67-
raise ValueError(msg + f': {node!r}')
68-
def _convert_num(node):
69-
if not isinstance(node, Constant) or type(node.value) not in (int, float, complex):
70-
_raise_malformed_node(node)
63+
return _convert_literal(node_or_string)
64+
65+
66+
def _convert_literal(node):
67+
"""
68+
Used by `literal_eval` to convert an AST node into a value.
69+
"""
70+
if isinstance(node, Constant):
7171
return node.value
72-
def _convert_signed_num(node):
73-
if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
74-
operand = _convert_num(node.operand)
75-
if isinstance(node.op, UAdd):
76-
return + operand
77-
else:
78-
return - operand
79-
return _convert_num(node)
80-
def _convert(node):
81-
if isinstance(node, Constant):
82-
return node.value
83-
elif isinstance(node, Tuple):
84-
return tuple(map(_convert, node.elts))
85-
elif isinstance(node, List):
86-
return list(map(_convert, node.elts))
87-
elif isinstance(node, Set):
88-
return set(map(_convert, node.elts))
89-
elif (isinstance(node, Call) and isinstance(node.func, Name) and
90-
node.func.id == 'set' and node.args == node.keywords == []):
91-
return set()
92-
elif isinstance(node, Dict):
93-
if len(node.keys) != len(node.values):
94-
_raise_malformed_node(node)
95-
return dict(zip(map(_convert, node.keys),
96-
map(_convert, node.values)))
97-
elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
98-
left = _convert_signed_num(node.left)
99-
right = _convert_num(node.right)
100-
if isinstance(left, (int, float)) and isinstance(right, complex):
101-
if isinstance(node.op, Add):
102-
return left + right
103-
else:
104-
return left - right
105-
return _convert_signed_num(node)
106-
return _convert(node_or_string)
72+
if isinstance(node, Dict) and len(node.keys) == len(node.values):
73+
return dict(zip(
74+
map(_convert_literal, node.keys),
75+
map(_convert_literal, node.values),
76+
))
77+
if isinstance(node, Tuple):
78+
return tuple(map(_convert_literal, node.elts))
79+
if isinstance(node, List):
80+
return list(map(_convert_literal, node.elts))
81+
if isinstance(node, Set):
82+
return set(map(_convert_literal, node.elts))
83+
if (
84+
isinstance(node, Call) and isinstance(node.func, Name)
85+
and node.func.id == 'set' and node.args == node.keywords == []
86+
):
87+
return set()
88+
if (
89+
isinstance(node, UnaryOp)
90+
and isinstance(node.op, (UAdd, USub))
91+
and isinstance(node.operand, Constant)
92+
and type(operand := node.operand.value) in (int, float, complex)
93+
):
94+
if isinstance(node.op, UAdd):
95+
return + operand
96+
else:
97+
return - operand
98+
if (
99+
isinstance(node, BinOp)
100+
and isinstance(node.op, (Add, Sub))
101+
and isinstance(node.left, (Constant, UnaryOp))
102+
and isinstance(node.right, Constant)
103+
and type(left := _convert_literal(node.left)) in (int, float)
104+
and type(right := _convert_literal(node.right)) is complex
105+
):
106+
if isinstance(node.op, Add):
107+
return left + right
108+
else:
109+
return left - right
110+
msg = "malformed node or string"
111+
if lno := getattr(node, 'lineno', None):
112+
msg += f' on line {lno}'
113+
raise ValueError(msg + f': {node!r}')
107114

108115

109116
def dump(

0 commit comments

Comments
 (0)