From cf11e81c9322659b4e489669d1ce4f605d90a993 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Wed, 14 Mar 2018 22:11:44 -0400 Subject: [PATCH 1/4] Remove unused imports --- hy/compiler.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 9a130f0..debf422 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -20,10 +20,8 @@ import hy.inspect import traceback import importlib -import codecs import ast import sys -import keyword import copy from collections import defaultdict From 84e1c65bcd7fb857dc3b2638f7ae9bb5f1eec7b1 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Wed, 14 Mar 2018 22:21:14 -0400 Subject: [PATCH 2/4] Improve checks inside `defclass` We need to make sure we're looking at HyExpression when trying to determine if we're rewriting an __init__ expression. Fixes #1533 --- hy/compiler.py | 5 ++++- tests/compilers/test_ast.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hy/compiler.py b/hy/compiler.py index debf422..9758d50 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2036,7 +2036,10 @@ class HyASTCompiler(object): def compile_class_expression(self, expressions): def rewire_init(expr): new_args = [] - if expr[0] == HySymbol("setv"): + if (isinstance(expr, HyExpression) + and len(expr) > 1 + and isinstance(expr[0], HySymbol) + and expr[0] == HySymbol("setv")): pairs = expr[1:] while len(pairs) > 0: k, v = (pairs.pop(0), pairs.pop(0)) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 2aea3a9..c779fbd 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -212,6 +212,9 @@ def test_ast_good_defclass(): "Make sure AST can compile valid defclass" can_compile("(defclass a)") can_compile("(defclass a [])") + can_compile("(defclass a [] None 42)") + can_compile("(defclass a [] None \"test\")") + can_compile("(defclass a [] None (print \"foo\"))") @pytest.mark.skipif(not PY3, reason="Python 3 supports class keywords") From b27cdfed6984e6d84e783af9e389c5eb3e30088c Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Fri, 23 Mar 2018 10:07:08 -0700 Subject: [PATCH 3/4] Fix `defclass` construction Python Class AST expects a body which is a list of ast.Expr. Force every entry to be stored as a statement. This means we'll preserve print statements. Python also doesn't construct docstrings in classes by setting a __doc__ attribute, it does it by inspecting the first ast.Expr node of the class. But this means we can remove the special handler for it. --- NEWS.rst | 1 + hy/compiler.py | 10 +++------- tests/native_tests/defclass.hy | 11 +++++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 94a561f..1be077e 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -27,6 +27,7 @@ Bug Fixes ------------------------------ * Fix `(return)` so it works correctly to exit a Python 2 generator * Fixed a case where `->` and `->>` duplicated an argument +* Fixed bugs that caused `defclass` to drop statements or crash Misc. Improvements ---------------------------- diff --git a/hy/compiler.py b/hy/compiler.py index 9758d50..d2f7fd0 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2072,12 +2072,7 @@ class HyASTCompiler(object): # grab the doc string, if there is one if expressions and isinstance(expressions[0], HyString): - docstring = expressions.pop(0) - symb = HySymbol("__doc__") - symb.start_line = docstring.start_line - symb.start_column = docstring.start_column - body += self._compile_assign(symb, docstring) - body += body.expr_as_stmt() + body += self.compile(expressions.pop(0)).expr_as_stmt() if expressions and isinstance(expressions[0], HyList) \ and not isinstance(expressions[0], HyExpression): @@ -2088,7 +2083,8 @@ class HyASTCompiler(object): body += self.compile(rewire_init(expr)) for expression in expressions: - body += self.compile(rewire_init(macroexpand(expression, self))) + e = self.compile(rewire_init(macroexpand(expression, self))) + body += e + e.expr_as_stmt() if not body.stmts: body += asty.Pass(expressions) diff --git a/tests/native_tests/defclass.hy b/tests/native_tests/defclass.hy index da3d9ad..b07e991 100644 --- a/tests/native_tests/defclass.hy +++ b/tests/native_tests/defclass.hy @@ -127,3 +127,14 @@ (setv b (B)) (assert (= a.x 1)) (assert (= b.x 2))) + +(defn test-class-sideeffects [] + "NATIVE: test that defclass runs all expressions" + (defn set-sentinel [] + (setv set-sentinel.set True)) + (setv set-sentinel.set False) + + (defclass A [] + (set-sentinel)) + + (assert set-sentinel.set)) From 258a1f57482cb6f2064a61b05d091e0f16b63496 Mon Sep 17 00:00:00 2001 From: Simon Gomizelj Date: Sat, 24 Mar 2018 15:19:29 -0400 Subject: [PATCH 4/4] Fix PY37 docstring generation --- hy/compiler.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index d2f7fd0..6f42e2c 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2071,8 +2071,12 @@ class HyASTCompiler(object): body = Result() # grab the doc string, if there is one + docstring = None if expressions and isinstance(expressions[0], HyString): - body += self.compile(expressions.pop(0)).expr_as_stmt() + docstring = expressions.pop(0) + if not PY37: + body += self.compile(docstring).expr_as_stmt() + docstring = None if expressions and isinstance(expressions[0], HyList) \ and not isinstance(expressions[0], HyExpression): @@ -2097,7 +2101,8 @@ class HyASTCompiler(object): starargs=None, kwargs=None, bases=bases_expr, - body=body.stmts) + body=body.stmts, + docstring=(None if docstring is None else str_type(docstring))) @builds("dispatch-tag-macro") @checkargs(exact=2)