From ea5eba59165df74b36351c1acd54126e08b2ff5a Mon Sep 17 00:00:00 2001 From: Bob Tolbert Date: Sun, 22 Jun 2014 14:33:39 -0600 Subject: [PATCH] Second part of the fix for yield inside a try-finally As noted in #600, Python 3 allows a return inside a generator method, that raises a StopIteration and passes the return value inside the 'value' attr of the exception. To allow this behaviour we simple set 'contains_yield' while compiling 'yield', thus allowing a return statement, but only for Python 3. Then when compiling the try-except, we check for contains_yield to decide whether there will be a return. This allows code like: (defn gen [] (yield 3) "goodbye") to compile in both Py2 and Py3. The return value is simply ignored in Python 2. hy2py in Python 2 gives: def g(): yield 3L u'goodbye' while hy2py in Python 3 gives: def g(): yield 3 return 'goodbye' Turns out return in yield started in Python 3.3 --- hy/compiler.py | 5 ++++- tests/native_tests/language.hy | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/hy/compiler.py b/hy/compiler.py index a4297e8..a82d89e 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -997,7 +997,10 @@ class HyASTCompiler(object): @checkargs(max=1) def compile_yield_expression(self, expr): expr.pop(0) - ret = Result(contains_yield=True) + if PY33: + ret = Result(contains_yield=False) + else: + ret = Result(contains_yield=True) value = None if expr != []: diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index b655ba6..7db5320 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -3,6 +3,7 @@ [sys :as systest]) (import sys) +(import [hy._compat [PY33 PY34]]) (defn test-sys-argv [] "NATIVE: test sys.argv" @@ -448,6 +449,29 @@ (for [y (gen)] (setv ret (+ ret y))) (assert (= ret 10))) +(defn test-yield-with-return [] + "NATIVE: test yield with return" + (defn gen [] (yield 3) "goodbye") + (if PY33 + (do (setv gg (gen)) + (assert (= 3 (next gg))) + (try (next gg) + (except [e StopIteration] (assert (hasattr e "value")) + (assert (= (getattr e "value") "goodbye"))))) + (do (setv gg (gen)) + (assert (= 3 (next gg))) + (try (next gg) + (except [e StopIteration] (assert (not (hasattr e "value")))))))) + + +(defn test-yield-in-try [] + "NATIVE: test yield in try" + (defn gen [] + (let [[x 1]] + (try (yield x) + (finally (print x))))) + (setv output (list (gen))) + (assert (= [1] output))) (defn test-first [] "NATIVE: test firsty things"