Properly add yield-from, using python's real "yield from".

The yield-from that existed previously wasn't actually implementing the
full complexity of "yield from":

  http://legacy.python.org/dev/peps/pep-0380/#formal-semantics

... this includes passing along errors, and many other things.

Also removes the yield-from backport macro, since it does not seem
possible at present to conditionally build macros.

Thus, there is no longer yield-from on pre-python-3.3 systems.

Includes updated docs and tests to reflect all this.
This commit is contained in:
Christopher Allan Webber 2014-02-23 10:48:33 -06:00
parent 5de39a4e1d
commit d5194f23fa
4 changed files with 53 additions and 13 deletions

View File

@ -1227,6 +1227,20 @@ infinite series without consuming infinite amount of memory.
=> (list-comp x [x (take 15 (random-numbers 1 50))])])
[7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19]
yield-from
----------
.. versionadded:: 0.9.13
**PYTHON 3.3 AND UP ONLY!**
`yield-from` is used to call a subgenerator. This is useful if you
want your coroutine to be able to delegate its processes to another
coroutine, say if using something fancy like
`asyncio <http://docs.python.org/3.4/library/asyncio.html>`_.
.. _zipwith:
zipwith

View File

@ -1017,6 +1017,28 @@ class HyASTCompiler(object):
return ret
@builds("yield_from")
@checkargs(max=1)
def compile_yield_from_expression(self, expr):
if not PY33:
raise HyCompileError(
"yield-from only supported in python 3.3+!")
expr.pop(0)
ret = Result(contains_yield=True)
value = None
if expr != []:
ret += self.compile(expr.pop(0))
value = ret.force_expr
ret += ast.YieldFrom(
value=value,
lineno=expr.start_line,
col_offset=expr.start_column)
return ret
@builds("import")
def compile_import_expression(self, expr):
def _compile_import(expr, module, names=None, importer=ast.Import):

View File

@ -27,8 +27,8 @@
(import [hy.models.list [HyList]]
[hy.models.symbol [HySymbol]])
[hy.models.symbol [HySymbol]]
[hy._compat [PY33 PY34]])
(defmacro for [args &rest body]
@ -155,12 +155,6 @@
`(if-not ~test (do ~@body)))
(defmacro yield-from [iterable]
"Yield all the items from iterable"
(let [[x (gensym)]]
`(for* [~x ~iterable]
(yield ~x))))
(defmacro with-gensyms [args &rest body]
`(let ~(HyList (map (fn [x] `[~x (gensym '~x)]) args))
~@body))

View File

@ -1,3 +1,6 @@
(import [hy._compat [PY33]])
(import [hy.errors [HyCompileError]])
(defmacro rev [&rest body]
"Execute the `body` statements in reverse"
(quasiquote (do (unquote-splice (list (reversed body))))))
@ -99,11 +102,18 @@
(defn test-yield-from []
"NATIVE: testing yield from"
(defn yield-from-test []
(for* [i (range 3)]
(yield i))
(yield-from [1 2 3]))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3])))
(try
(eval
'(do (defn yield-from-test []
(for* [i (range 3)]
(yield i))
(yield-from [1 2 3]))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3]))))
(catch [e HyCompileError]
;; Yup, this should happen on non-Python3.3 thingies
(assert (not PY33)))
(else (assert PY33))))
(defn test-if-python2 []
(import sys)