Skip to content

Commit 76659fd

Browse files
Merge pull request RustPython#820 from skinny121/gen_throw
Implement generator.throw
2 parents 456c8a1 + 63643e1 commit 76659fd

File tree

4 files changed

+89
-23
lines changed

4 files changed

+89
-23
lines changed

tests/snippets/generators.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from testutils import assertRaises
2+
13

24
r = []
35

@@ -42,3 +44,23 @@ def g4():
4244
r = list(g4())
4345
assert r == [None, (2,)]
4446

47+
48+
def catch_exception():
49+
try:
50+
yield 1
51+
except ValueError:
52+
yield 2
53+
yield 3
54+
55+
56+
g = catch_exception()
57+
assert next(g) == 1
58+
59+
assert g.throw(ValueError, ValueError(), None) == 2
60+
assert next(g) == 3
61+
62+
g = catch_exception()
63+
assert next(g) == 1
64+
65+
with assertRaises(KeyError):
66+
assert g.throw(KeyError, KeyError(), None) == 2

vm/src/frame.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,17 @@ impl Frame {
283283
}
284284
}
285285

286+
pub fn throw(
287+
&self,
288+
vm: &VirtualMachine,
289+
exception: PyObjectRef,
290+
) -> Result<ExecutionResult, PyObjectRef> {
291+
match self.unwind_exception(vm, exception) {
292+
None => self.run(vm),
293+
Some(exception) => Err(exception),
294+
}
295+
}
296+
286297
pub fn fetch_instruction(&self) -> &bytecode::Instruction {
287298
let ins2 = &self.code.instructions[*self.lasti.borrow()];
288299
*self.lasti.borrow_mut() += 1;

vm/src/obj/objgenerator.rs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
*/
44

55
use crate::frame::{ExecutionResult, FrameRef};
6-
use crate::obj::objtype::PyClassRef;
7-
use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue};
6+
use crate::obj::objtype::{isinstance, PyClassRef};
7+
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
88
use crate::vm::VirtualMachine;
99

1010
pub type PyGeneratorRef = PyRef<PyGenerator>;
1111

12+
#[pyclass(name = "generator", __inside_vm)]
1213
#[derive(Debug)]
1314
pub struct PyGenerator {
1415
frame: FrameRef,
@@ -20,38 +21,59 @@ impl PyValue for PyGenerator {
2021
}
2122
}
2223

23-
impl PyGeneratorRef {
24+
#[pyimpl(__inside_vm)]
25+
impl PyGenerator {
2426
pub fn new(frame: FrameRef, vm: &VirtualMachine) -> PyGeneratorRef {
2527
PyGenerator { frame }.into_ref(vm)
2628
}
2729

28-
fn iter(self, _vm: &VirtualMachine) -> PyGeneratorRef {
29-
self
30+
#[pymethod(name = "__iter__")]
31+
fn iter(zelf: PyGeneratorRef, _vm: &VirtualMachine) -> PyGeneratorRef {
32+
zelf
3033
}
3134

32-
fn next(self, vm: &VirtualMachine) -> PyResult {
35+
#[pymethod(name = "__next__")]
36+
fn next(&self, vm: &VirtualMachine) -> PyResult {
3337
self.send(vm.get_none(), vm)
3438
}
3539

36-
fn send(self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
40+
#[pymethod]
41+
fn send(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
3742
self.frame.push_value(value.clone());
3843

39-
match vm.run_frame(self.frame.clone())? {
40-
ExecutionResult::Yield(value) => Ok(value),
41-
ExecutionResult::Return(_value) => {
42-
// Stop iteration!
43-
let stop_iteration = vm.ctx.exceptions.stop_iteration.clone();
44-
Err(vm.new_exception(stop_iteration, "End of generator".to_string()))
45-
}
44+
let result = vm.run_frame(self.frame.clone())?;
45+
handle_execution_result(result, vm)
46+
}
47+
48+
#[pymethod]
49+
fn throw(
50+
&self,
51+
_exc_type: PyObjectRef,
52+
exc_val: PyObjectRef,
53+
_exc_tb: PyObjectRef,
54+
vm: &VirtualMachine,
55+
) -> PyResult {
56+
// TODO what should we do with the other parameters? CPython normalises them with
57+
// PyErr_NormalizeException, do we want to do the same.
58+
if !isinstance(&exc_val, &vm.ctx.exceptions.base_exception_type) {
59+
return Err(vm.new_type_error("Can't throw non exception".to_string()));
60+
}
61+
let result = vm.frame_throw(self.frame.clone(), exc_val)?;
62+
handle_execution_result(result, vm)
63+
}
64+
}
65+
66+
fn handle_execution_result(result: ExecutionResult, vm: &VirtualMachine) -> PyResult {
67+
match result {
68+
ExecutionResult::Yield(value) => Ok(value),
69+
ExecutionResult::Return(_value) => {
70+
// Stop iteration!
71+
let stop_iteration = vm.ctx.exceptions.stop_iteration.clone();
72+
Err(vm.new_exception(stop_iteration, "End of generator".to_string()))
4673
}
4774
}
4875
}
4976

50-
pub fn init(context: &PyContext) {
51-
let generator_type = &context.generator_type;
52-
extend_class!(context, generator_type, {
53-
"__iter__" => context.new_rustfunc(PyGeneratorRef::iter),
54-
"__next__" => context.new_rustfunc(PyGeneratorRef::next),
55-
"send" => context.new_rustfunc(PyGeneratorRef::send)
56-
});
77+
pub fn init(ctx: &PyContext) {
78+
PyGenerator::extend_class(ctx, &ctx.generator_type);
5779
}

vm/src/vm.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::obj::objbuiltinfunc::PyBuiltinFunction;
2121
use crate::obj::objcode::PyCodeRef;
2222
use crate::obj::objdict::PyDictRef;
2323
use crate::obj::objfunction::{PyFunction, PyMethod};
24-
use crate::obj::objgenerator::PyGeneratorRef;
24+
use crate::obj::objgenerator::PyGenerator;
2525
use crate::obj::objiter;
2626
use crate::obj::objsequence;
2727
use crate::obj::objstr::{PyString, PyStringRef};
@@ -94,6 +94,17 @@ impl VirtualMachine {
9494
result
9595
}
9696

97+
pub fn frame_throw(
98+
&self,
99+
frame: FrameRef,
100+
exception: PyObjectRef,
101+
) -> PyResult<ExecutionResult> {
102+
self.frames.borrow_mut().push(frame.clone());
103+
let result = frame.throw(self, exception);
104+
self.frames.borrow_mut().pop();
105+
result
106+
}
107+
97108
pub fn current_frame(&self) -> Option<Ref<FrameRef>> {
98109
let frames = self.frames.borrow();
99110
if frames.is_empty() {
@@ -374,7 +385,7 @@ impl VirtualMachine {
374385

375386
// If we have a generator, create a new generator
376387
if code.code.is_generator {
377-
Ok(PyGeneratorRef::new(frame, self).into_object())
388+
Ok(PyGenerator::new(frame, self).into_object())
378389
} else {
379390
self.run_frame_full(frame)
380391
}

0 commit comments

Comments
 (0)