gh-124503: Optimize ast.literal_eval() for small input (GH-137010) · python/cpython@b723c8b

GitHub

@@ -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 """

5959ifisinstance(node_or_string, str):

60-node_or_string=parse(node_or_string.lstrip(" \t"), mode='eval')

61-ifisinstance(node_or_string, Expression):

60+node_or_string=parse(node_or_string.lstrip(" \t"), mode='eval').body

61+elifisinstance(node_or_string, Expression):

6262node_or_string=node_or_string.body

63-def_raise_malformed_node(node):

64-msg="malformed node or string"

65-iflno:=getattr(node, 'lineno', None):

66-msg+=f' on line {lno}'

67-raiseValueError(msg+f': {node!r}')

68-def_convert_num(node):

69-ifnotisinstance(node, Constant) ortype(node.value) notin (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+ifisinstance(node, Constant):

7171returnnode.value

72-def_convert_signed_num(node):

73-ifisinstance(node, UnaryOp) andisinstance(node.op, (UAdd, USub)):

74-operand=_convert_num(node.operand)

75-ifisinstance(node.op, UAdd):

76-return+operand

77-else:

78-return-operand

79-return_convert_num(node)

80-def_convert(node):

81-ifisinstance(node, Constant):

82-returnnode.value

83-elifisinstance(node, Tuple):

84-returntuple(map(_convert, node.elts))

85-elifisinstance(node, List):

86-returnlist(map(_convert, node.elts))

87-elifisinstance(node, Set):

88-returnset(map(_convert, node.elts))

89-elif (isinstance(node, Call) andisinstance(node.func, Name) and

90-node.func.id=='set'andnode.args==node.keywords== []):

91-returnset()

92-elifisinstance(node, Dict):

93-iflen(node.keys) !=len(node.values):

94-_raise_malformed_node(node)

95-returndict(zip(map(_convert, node.keys),

96-map(_convert, node.values)))

97-elifisinstance(node, BinOp) andisinstance(node.op, (Add, Sub)):

98-left=_convert_signed_num(node.left)

99-right=_convert_num(node.right)

100-ifisinstance(left, (int, float)) andisinstance(right, complex):

101-ifisinstance(node.op, Add):

102-returnleft+right

103-else:

104-returnleft-right

105-return_convert_signed_num(node)

106-return_convert(node_or_string)

72+ifisinstance(node, Dict) andlen(node.keys) ==len(node.values):

73+returndict(zip(

74+map(_convert_literal, node.keys),

75+map(_convert_literal, node.values),

76+ ))

77+ifisinstance(node, Tuple):

78+returntuple(map(_convert_literal, node.elts))

79+ifisinstance(node, List):

80+returnlist(map(_convert_literal, node.elts))

81+ifisinstance(node, Set):

82+returnset(map(_convert_literal, node.elts))

83+if (

84+isinstance(node, Call) andisinstance(node.func, Name)

85+andnode.func.id=='set'andnode.args==node.keywords== []

86+ ):

87+returnset()

88+if (

89+isinstance(node, UnaryOp)

90+andisinstance(node.op, (UAdd, USub))

91+andisinstance(node.operand, Constant)

92+andtype(operand:=node.operand.value) in (int, float, complex)

93+ ):

94+ifisinstance(node.op, UAdd):

95+return+operand

96+else:

97+return-operand

98+if (

99+isinstance(node, BinOp)

100+andisinstance(node.op, (Add, Sub))

101+andisinstance(node.left, (Constant, UnaryOp))

102+andisinstance(node.right, Constant)

103+andtype(left:=_convert_literal(node.left)) in (int, float)

104+andtype(right:=_convert_literal(node.right)) iscomplex

105+ ):

106+ifisinstance(node.op, Add):

107+returnleft+right

108+else:

109+returnleft-right

110+msg="malformed node or string"

111+iflno:=getattr(node, 'lineno', None):

112+msg+=f' on line {lno}'

113+raiseValueError(msg+f': {node!r}')

107114108115109116defdump(