package org.python.core;
/**
* Straightens the call path for some common cases
*/
public class ContextGuard implements ContextManager {
private final PyObject __enter__method;
private final PyObject __exit__method;
private ContextGuard(PyObject manager) {
__exit__method = manager.__getattr__("__exit__");
__enter__method = manager.__getattr__("__enter__");
}
public PyObject __enter__(ThreadState ts) {
return __enter__method.__call__(ts);
}
public boolean __exit__(ThreadState ts, PyException exception) {
final PyObject type, value, traceback;
if (exception != null) {
type = exception.type;
value = exception.value;
traceback = exception.traceback;
} else {
type = value = traceback = Py.None;
}
return __exit__method.__call__(ts, type,
value == null ? Py.None : value,
traceback == null ? Py.None : traceback).__nonzero__();
}
public static ContextManager getManager(PyObject manager) {
if (manager instanceof ContextManager) {
return (ContextManager) manager;
} else {
return new ContextGuard(manager);
}
}
// XXX - tentative support for generators in conjunction w/ contextlib.contextmanager
/* Sample usage:
*
* from org.python.core.ContextGuard import makeManager as contextmanager
* @contextmanager
* def my_manager():
* print "setup"
* try:
* yield
* finally:
* print "done"
*/
public static PyObject makeManager(PyObject object) {
if (object instanceof PyFunction) {
PyFunction function = (PyFunction) object;
PyCode code = function.func_code;
if (code instanceof PyBaseCode) {
PyBaseCode pyCode = (PyBaseCode) code;
if (pyCode.co_flags.isFlagSet(CodeFlag.CO_GENERATOR)) {
return new PyFunction(function.func_globals,
function.func_defaults,
new ContextCode(pyCode),
function.__doc__,
(function.func_closure == null) ? null :
((PyTuple)function.func_closure).getArray());
}
}
}
throw Py.TypeError("Argument must be a generator function.");
}
private static class ContextCode extends PyBaseCode {
private final PyBaseCode code;
ContextCode(PyBaseCode code) {
this.co_name = code.co_name;
this.code = code;
this.co_argcount = code.co_argcount;
this.nargs = code.nargs;
this.co_firstlineno = code.co_firstlineno;
this.co_varnames = code.co_varnames;
this.co_cellvars = code.co_cellvars;
this.jy_npurecell = code.jy_npurecell;
this.co_freevars = code.co_freevars;
this.co_filename = code.co_filename;
this.co_nlocals = code.co_nlocals;
this.varargs = code.varargs;
this.varkwargs = code.varkwargs;
for (CodeFlag flag : CodeFlag.values()) {
if (code.co_flags.isFlagSet(flag) && flag != CodeFlag.CO_GENERATOR) {
this.co_flags.setFlag(flag);
}
}
}
@SuppressWarnings("serial")
@Override
protected PyObject interpret(PyFrame frame, ThreadState ts) {
frame.f_back = null;
return new GeneratorContextManager(frame) {
@Override
PyObject body(ThreadState ts) {
return code.interpret(frame, ts);
}
};
}
@SuppressWarnings("serial")
@Override
public PyObject call(ThreadState ts, PyFrame frame, final PyObject closure) {
frame.f_back = null;
return new GeneratorContextManager(frame) {
@Override
PyObject body(ThreadState ts) {
return code.call(ts, frame, closure);
}
};
}
}
@SuppressWarnings("serial")
private static abstract class GeneratorContextManager extends PyObject implements ContextManager {
final PyFrame frame;
public GeneratorContextManager(PyFrame frame) {
this.frame = frame;
}
public PyObject __enter__(ThreadState ts) {
PyObject res = body(ts);
if (frame.f_lasti == -1) {
throw Py.RuntimeError("generator didn't yield");
}
return res;
}
public boolean __exit__(ThreadState ts, PyException exception) {
if (exception != null) {
frame.setGeneratorInput(exception);
}
final PyObject res;
try {
res = body(ts);
} catch(PyException e) {
if (e.equals(exception)) {
return false;
} else {
throw e;
}
}
if (frame.f_lasti != -1) {
throw Py.RuntimeError("generator didn't stop");
}
return res.__nonzero__();
}
abstract PyObject body(ThreadState ts);
}
}