berkerpeksag / astor

Python AST read/write
https://pypi.org/project/astor/
BSD 3-Clause "New" or "Revised" License
810 stars 102 forks source link

Wrong treatment of "big" double literals #82

Closed piedenis closed 6 years ago

piedenis commented 7 years ago

I am facing a subtle problem when a big float literal is converted to infinity by Python. This is illustrated in the following snippet (tested with astor 0.5 on Python 3.4):

print (astor.to_source(ast.parse('a = 1e400')))
# --> 'a = inf'
print (astor.to_source(ast.parse('a = -1e400')))
# --> 'a = (- inf)'

These round-trip calls are supposed to produce equivalent Python code that is syntactically valid. This is not the case here since inf is not a builtin object of Python. Actually, the bug occurs because the infinity value is already in the ast node; I assume that astor.to_source calls repr on the value carried by this node, which indeed returns the string 'inf'. A satisfying solution for me could be this:

print (astor.to_source(ast.parse('a = 1e400')))
# --> "a = float('inf')"
print (astor.to_source(ast.parse('a = -1e400')))
# --> "a = - float('inf')"
berkerpeksag commented 6 years ago

This looks more broken in astor 0.6:

>>> import astor
>>> import ast
>>> print (astor.to_source(ast.parse('a = 1e400')))
a = 1e1000

>>> print (astor.to_source(ast.parse('a = -1e400')))
a = -1e1000

@pmaupin do you know what causes this?

pmaupin commented 6 years ago

@berkerpeksag :

Sorry I didn't document this fix better.

As described by the bug report, the behavior in 0.5 is broken -- inf can appear in the AST, but inf is not a reserved word in Python.

However, the suggested fix of using float('inf') will not round-trip properly; the call to float is embedded in the AST along with the string 'inf', so the generated AST does not match the original AST.

The only way I know of to create source code that will cause inf to be output in the AST is to use a number that is too big, so the current solution works and round-trips correctly, unless somebody has a python with more than 10 bits of exponent in a float. If anybody knows a more canonical way of doing this, that would be great, but the goal is to generate source code that will compile to having inf in the AST, not to generate a call to float along with a string.

BTW, thanks for all your great work on this!

berkerpeksag commented 6 years ago

Ah, you're right. I definitely missed this:

https://github.com/berkerpeksag/astor/blob/50d6d27fed5a4486691e36f4e876c99741b1a4c3/astor/code_gen.py#L612-L618

I've added a test case and explicitly document this in the test so hopefully I won't forget again :)

Thanks!