[pypy-commit] pypy py3.3: Implement "yield from" opcode
amauryfa
noreply at buildbot.pypy.org
Thu Jul 3 23:58:09 CEST 2014
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.3
Changeset: r72339:02025d9b1f7d
Date: 2014-06-22 19:08 +0200
http://bitbucket.org/pypy/pypy/changeset/02025d9b1f7d/
Log: Implement "yield from" opcode
diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -871,11 +871,12 @@
self.load_const(self.space.w_None)
self.emit_op(ops.YIELD_VALUE)
- def visit_YieldFrom(self, yie):
- # XXX not correctly implemented.
- self.update_position(yie.lineno)
- yie.value.walkabout(self)
- self.emit_op(ops.YIELD_VALUE)
+ def visit_YieldFrom(self, yfr):
+ self.update_position(yfr.lineno)
+ yfr.value.walkabout(self)
+ self.emit_op(ops.GET_ITER)
+ self.load_const(self.space.w_None)
+ self.emit_op(ops.YIELD_FROM)
def visit_Num(self, num):
self.update_position(num.lineno)
diff --git a/pypy/interpreter/astcompiler/symtable.py b/pypy/interpreter/astcompiler/symtable.py
--- a/pypy/interpreter/astcompiler/symtable.py
+++ b/pypy/interpreter/astcompiler/symtable.py
@@ -431,6 +431,10 @@
self.scope.note_yield(yie)
ast.GenericASTVisitor.visit_Yield(self, yie)
+ def visit_YieldFrom(self, yfr):
+ self.scope.note_yield(yfr)
+ ast.GenericASTVisitor.visit_YieldFrom(self, yfr)
+
def visit_Global(self, glob):
for name in glob.names:
old_role = self.scope.lookup_role(name)
diff --git a/pypy/interpreter/astcompiler/test/test_astbuilder.py b/pypy/interpreter/astcompiler/test/test_astbuilder.py
--- a/pypy/interpreter/astcompiler/test/test_astbuilder.py
+++ b/pypy/interpreter/astcompiler/test/test_astbuilder.py
@@ -928,7 +928,6 @@
expr = self.get_first_expr("yield")
assert isinstance(expr, ast.Yield)
assert expr.value is None
- assert expr.is_from == 0
expr = self.get_first_expr("yield x")
assert isinstance(expr.value, ast.Name)
assign = self.get_first_stmt("x = yield x")
@@ -937,8 +936,8 @@
def test_yield_from(self):
expr = self.get_first_expr("yield from x")
+ assert isinstance(expr, ast.YieldFrom)
assert isinstance(expr.value, ast.Name)
- assert expr.is_from == 1
def test_unaryop(self):
unary_ops = (
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -965,6 +965,15 @@
yield self.st, 'x = list(d for d in [1] or [])', 'x', [1]
yield self.st, 'y = [d for d in [1] or []]', 'y', [1]
+ def test_yield_from(self):
+ test = """if 1:
+ def f():
+ yield from range(3)
+ def g():
+ return list(f())
+ """
+ yield self.st, test, "g()", range(3)
+
class AppTestCompiler:
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -401,6 +401,8 @@
self.WITH_CLEANUP(oparg, next_instr)
elif opcode == opcodedesc.YIELD_VALUE.index:
self.YIELD_VALUE(oparg, next_instr)
+ elif opcode == opcodedesc.YIELD_FROM.index:
+ self.YIELD_FROM(oparg, next_instr)
else:
self.MISSING_OPCODE(oparg, next_instr)
@@ -1000,6 +1002,34 @@
def YIELD_VALUE(self, oparg, next_instr):
raise Yield
+ def YIELD_FROM(self, oparg, next_instr):
+ space = self.space
+ w_value = self.popvalue()
+ w_gen = self.peekvalue()
+ try:
+ if space.is_none(w_value):
+ w_retval = space.next(w_gen)
+ else:
+ w_retval = space.call_method(w_gen, "send", w_value)
+ except OperationError as e:
+ if not e.match(self.space, self.space.w_StopIteration):
+ raise
+ self.popvalue() # Remove iter from stack
+ try:
+ w_value = space.getattr(e.get_w_value(space), space.wrap("value"))
+ except OperationError as e:
+ if not e.match(self.space, self.space.w_AttributeError):
+ raise
+ w_value = space.w_None
+ self.pushvalue(w_value)
+ return next_instr
+ else:
+ # iter remains on stack, w_retval is value to be yielded.
+ self.pushvalue(w_retval)
+ # and repeat...
+ self.last_instr = self.last_instr - 1
+ raise Yield
+
def jump_absolute(self, jumpto, ec):
# this function is overridden by pypy.module.pypyjit.interp_jit
check_nonneg(jumpto)
More information about the pypy-commit
mailing list