Repository: hylang/hy

Issue:
Astor update 0.6 10/31 change in API (astor.codegen to astor.code_gen) breaks hy.cmdline
Good timing, started a Hy project today and noticed the change.

Either update the import or specify version 0.5 as the requirement.


Discussion:
I'd prefer we use 0.6 if possible. It has some nice improvements.
I tried updating Hy to astor 0.6, but it's choking on `NaN`.

Resolving patch:
diff --git a/hy/cmdline.py b/hy/cmdline.py
--- a/hy/cmdline.py
+++ b/hy/cmdline.py
@@ -11,7 +11,7 @@
 import os
 import importlib
 
-import astor.codegen
+import astor.code_gen
 
 import hy
 
@@ -413,17 +413,17 @@ def hy2py_main():
             else pretty_error(import_buffer_to_ast, stdin_text, module_name))
     if options.with_ast:
         if PY3 and platform.system() == "Windows":
-            _print_for_windows(astor.dump(_ast))
+            _print_for_windows(astor.dump_tree(_ast))
         else:
-            print(astor.dump(_ast))
+            print(astor.dump_tree(_ast))
         print()
         print()
 
     if not options.without_python:
         if PY3 and platform.system() == "Windows":
-            _print_for_windows(astor.codegen.to_source(_ast))
+            _print_for_windows(astor.code_gen.to_source(_ast))
         else:
-            print(astor.codegen.to_source(_ast))
+            print(astor.code_gen.to_source(_ast))
 
     parser.exit(0)
 
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -27,6 +27,7 @@
 import inspect
 
 from collections import defaultdict
+from cmath import isnan
 
 if PY3:
     import builtins
@@ -2155,11 +2156,27 @@ def compile_cons(self, cons):
         raise HyTypeError(cons, "Can't compile a top-level cons cell")
 
     @builds(HyInteger, HyFloat, HyComplex)
-    def compile_numeric_literal(self, number, building):
+    def compile_numeric_literal(self, x, building):
         f = {HyInteger: long_type,
              HyFloat: float,
              HyComplex: complex}[building]
-        return asty.Num(number, n=f(number))
+        # Work around https://github.com/berkerpeksag/astor/issues/85 :
+        # astor can't generate Num nodes with NaN, so we have
+        # to build an expression that evaluates to NaN.
+        def nn(number):
+            return asty.Num(x, n=number)
+        if isnan(x):
+            def nan(): return asty.BinOp(
+                  x, left=nn(1e900), op=ast.Sub(), right=nn(1e900))
+            if f is complex:
+                return asty.Call(
+                    x,
+                    func=asty.Name(x, id="complex", ctx=ast.Load()),
+                    keywords=[],
+                    args=[nan() if isnan(x.real) else nn(x.real),
+                          nan() if isnan(x.imag) else nn(x.imag)])
+            return nan()
+        return nn(f(x))
 
     @builds(HySymbol)
     def compile_symbol(self, symbol):
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -30,7 +30,7 @@ def run(self):
                         "." + filename[:-len(".hy")])
         install.run(self)
 
-install_requires = ['rply>=0.7.5', 'astor>=0.5', 'clint>=0.4']
+install_requires = ['rply>=0.7.5', 'astor>=0.6', 'clint>=0.4']
 if os.name == 'nt':
     install_requires.append('pyreadline>=2.1')
 


Test patch:
diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy
--- a/tests/native_tests/language.hy
+++ b/tests/native_tests/language.hy
@@ -1550,13 +1550,23 @@
   "NATIVE: Test the disassemble function"
   (if PY35
     (assert (= (disassemble '(do (leaky) (leaky) (macros)))
-               "Module(\n    body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),\n        Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),\n        Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))])"))
+               "Module(
+    body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
+        Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
+        Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))])"))
     (assert (= (disassemble '(do (leaky) (leaky) (macros)))
-               "Module(\n    body=[\n        Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),\n        Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),\n        Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])")))
+               "Module(
+    body=[
+        Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
+        Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
+        Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])")))
   (assert (= (disassemble '(do (leaky) (leaky) (macros)) True)
-             "leaky()\nleaky()\nmacros()"))
+             "leaky()
+leaky()
+macros()
+"))
   (assert (= (re.sub r"[L() ]" "" (disassemble `(+ ~(+ 1 1) 40) True))
-             "2+40")))
+             "2+40\n")))
 
 
 (defn test-attribute-access []
diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy
--- a/tests/native_tests/native_macros.hy
+++ b/tests/native_tests/native_macros.hy
@@ -144,7 +144,7 @@
 
 (defn test-gensym-in-macros []
   (import ast)
-  (import [astor.codegen [to_source]])
+  (import [astor.code-gen [to-source]])
   (import [hy.importer [import_buffer_to_ast]])
   (setv macro1 "(defmacro nif [expr pos zero neg]
       (setv g (gensym))
@@ -170,7 +170,7 @@
 
 (defn test-with-gensym []
   (import ast)
-  (import [astor.codegen [to_source]])
+  (import [astor.code-gen [to-source]])
   (import [hy.importer [import_buffer_to_ast]])
   (setv macro1 "(defmacro nif [expr pos zero neg]
       (with-gensyms [a]
@@ -194,7 +194,7 @@
 
 (defn test-defmacro-g! []
   (import ast)
-  (import [astor.codegen [to_source]])
+  (import [astor.code-gen [to-source]])
   (import [hy.importer [import_buffer_to_ast]])
   (setv macro1 "(defmacro/g! nif [expr pos zero neg]
         `(do
@@ -223,7 +223,7 @@
 (defn test-defmacro! []
   ;; defmacro! must do everything defmacro/g! can
   (import ast)
-  (import [astor.codegen [to_source]])
+  (import [astor.code-gen [to-source]])
   (import [hy.importer [import_buffer_to_ast]])
   (setv macro1 "(defmacro! nif [expr pos zero neg]
         `(do
diff --git a/tests/test_bin.py b/tests/test_bin.py
--- a/tests/test_bin.py
+++ b/tests/test_bin.py
@@ -72,7 +72,7 @@ def test_bin_hy_stdin():
 
     output, _ = run_cmd("hy --spy", '(koan)')
     assert "monk" in output
-    assert "\\n  Ummon" in output
+    assert "\n  Ummon" in output
 
     # --spy should work even when an exception is thrown
     output, _ = run_cmd("hy --spy", '(foof)')
@@ -195,7 +195,7 @@ def test_bin_hy_icmd_file():
 
 def test_bin_hy_icmd_and_spy():
     output, _ = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
-    assert "([] + [])" in output
+    assert "[] + []" in output
 
 
 def test_bin_hy_missing_file():