/* Copyright (c) Jython Developers */
package org.python.core;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedType;
@ExposedType(name = "generator", base = PyObject.class, isBaseType = false)
public class PyGenerator extends PyIterator {
public static final PyType TYPE = PyType.fromClass(PyGenerator.class);
@ExposedGet
protected PyFrame gi_frame;
@ExposedGet
protected boolean gi_running;
private PyObject closure;
public PyGenerator(PyFrame frame, PyObject closure) {
super(TYPE);
gi_frame = frame;
this.closure = closure;
}
public PyObject send(PyObject value) {
return generator_send(value);
}
@ExposedMethod
final PyObject generator_send(PyObject value) {
if (gi_frame == null) {
throw Py.StopIteration("");
}
if (gi_frame.f_lasti == 0 && value != Py.None) {
throw Py.TypeError("can't send non-None value to a just-started generator");
}
gi_frame.setGeneratorInput(value);
return next();
}
public PyObject throw$(PyObject type, PyObject value, PyObject tb) {
return generator_throw$(type, value, tb);
}
@ExposedMethod(names="throw", defaults={"null", "null"})
final PyObject generator_throw$(PyObject type, PyObject value, PyObject tb) {
if (tb == Py.None) {
tb = null;
} else if (tb != null && !(tb instanceof PyTraceback)) {
throw Py.TypeError("throw() third argument must be a traceback object");
}
return raiseException(Py.makeException(type, value, tb));
}
public PyObject close() {
return generator_close();
}
@ExposedMethod
final PyObject generator_close() {
try {
raiseException(Py.makeException(Py.GeneratorExit));
throw Py.RuntimeError("generator ignored GeneratorExit");
} catch (PyException e) {
if (!(e.type == Py.StopIteration || e.type == Py.GeneratorExit)) {
throw e;
}
}
return Py.None;
}
@Override
public PyObject next() {
return generator_next();
}
@ExposedMethod(doc="x.next() -> the next value, or raise StopIteration")
final PyObject generator_next() {
return super.next();
}
@Override
public PyObject __iter__() {
return generator___iter__();
}
@ExposedMethod
final PyObject generator___iter__() {
return this;
}
private PyObject raiseException(PyException ex) {
if (gi_frame == null || gi_frame.f_lasti == 0) {
gi_frame = null;
throw ex;
}
gi_frame.setGeneratorInput(ex);
return next();
}
@Override
protected void finalize() throws Throwable {
if (gi_frame == null || gi_frame.f_lasti == -1) {
return;
}
try {
close();
} catch (PyException pye) {
// PEP 342 specifies that if an exception is raised by close,
// we output to stderr and then forget about it;
String className = PyException.exceptionClassName(pye.type);
int lastDot = className.lastIndexOf('.');
if (lastDot != -1) {
className = className.substring(lastDot + 1);
}
String msg = String.format("Exception %s: %s in %s", className, pye.value.__repr__(),
__repr__());
Py.println(Py.getSystemState().stderr, Py.newString(msg));
} catch (Throwable t) {
// but we currently ignore any Java exception completely. perhaps we
// can also output something meaningful too?
} finally {
super.finalize();
}
}
@Override
public PyObject __iternext__() {
return __iternext__(Py.getThreadState());
}
public PyObject __iternext__(ThreadState state) {
if (gi_running) {
throw Py.ValueError("generator already executing");
}
if (gi_frame == null) {
return null;
}
if (gi_frame.f_lasti == -1) {
gi_frame = null;
return null;
}
gi_running = true;
PyObject result = null;
try {
result = gi_frame.f_code.call(state, gi_frame, closure);
} catch (PyException pye) {
if (!(pye.type == Py.StopIteration || pye.type == Py.GeneratorExit)) {
gi_frame = null;
throw pye;
} else {
stopException = pye;
gi_frame = null;
return null;
}
} finally {
gi_running = false;
}
if (result == Py.None && gi_frame.f_lasti == -1) {
return null;
}
return result;
}
}