mirror of https://github.com/kivy/pyjnius.git
Merge pull request #115 from Lenbok/issue-96-exception-handling
Issue 96 improved exception handling
This commit is contained in:
commit
9425a2cc31
|
@ -2,7 +2,15 @@
|
|||
class JavaException(Exception):
|
||||
'''Can be a real java exception, or just an exception from the wrapper.
|
||||
'''
|
||||
pass
|
||||
classname = None # The classname of the exception
|
||||
innermessage = None # The message of the inner exception
|
||||
stacktrace = None # The stack trace of the inner exception
|
||||
|
||||
def __init__(self, message, classname=None, innermessage=None, stacktrace=None):
|
||||
self.classname = classname
|
||||
self.innermessage = innermessage
|
||||
self.stacktrace = stacktrace
|
||||
Exception.__init__(self, message)
|
||||
|
||||
|
||||
cdef class JavaObject(object):
|
||||
|
|
|
@ -36,11 +36,80 @@ cdef parse_definition(definition):
|
|||
|
||||
|
||||
cdef void check_exception(JNIEnv *j_env) except *:
|
||||
cdef jmethodID toString = NULL
|
||||
cdef jmethodID getCause = NULL
|
||||
cdef jmethodID getStackTrace = NULL
|
||||
cdef jmethodID getMessage = NULL
|
||||
cdef jstring e_msg
|
||||
cdef jboolean isCopy
|
||||
cdef jthrowable exc = j_env[0].ExceptionOccurred(j_env)
|
||||
if exc:
|
||||
j_env[0].ExceptionDescribe(j_env)
|
||||
# ExceptionDescribe always writes to stderr, preventing tidy exception
|
||||
# handling, so should only be for debugging
|
||||
# j_env[0].ExceptionDescribe(j_env)
|
||||
j_env[0].ExceptionClear(j_env)
|
||||
raise JavaException('JVM exception occured')
|
||||
|
||||
toString = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Object"), "toString", "()Ljava/lang/String;");
|
||||
getMessage = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getMessage", "()Ljava/lang/String;");
|
||||
getCause = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getCause", "()Ljava/lang/Throwable;");
|
||||
getStackTrace = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getStackTrace", "()[Ljava/lang/StackTraceElement;");
|
||||
|
||||
e_msg = j_env[0].CallObjectMethod(j_env, exc, getMessage);
|
||||
pymsg = None if e_msg == NULL else convert_jobject_to_python(j_env, <bytes> 'Ljava/lang/String;', e_msg)
|
||||
|
||||
pystack = []
|
||||
_append_exception_trace_messages(j_env, pystack, exc, getCause, getStackTrace, toString)
|
||||
|
||||
pyexcclass = lookup_java_object_name(j_env, exc).replace('/', '.')
|
||||
|
||||
raise JavaException('JVM exception occurred: %s' % (pymsg if pymsg is not None else pyexcclass), pyexcclass, pymsg, pystack)
|
||||
|
||||
|
||||
cdef void _append_exception_trace_messages(
|
||||
JNIEnv* j_env,
|
||||
list pystack,
|
||||
jthrowable exc,
|
||||
jmethodID mid_getCause,
|
||||
jmethodID mid_getStackTrace,
|
||||
jmethodID mid_toString):
|
||||
|
||||
# Get the array of StackTraceElements.
|
||||
cdef jobjectArray frames = j_env[0].CallObjectMethod(j_env, exc, mid_getStackTrace)
|
||||
cdef jsize frames_length = j_env[0].GetArrayLength(j_env, frames)
|
||||
cdef jstring msg_obj
|
||||
cdef jobject frame
|
||||
cdef jthrowable cause
|
||||
|
||||
# Add Throwable.toString() before descending stack trace messages.
|
||||
if frames != NULL:
|
||||
msg_obj = j_env[0].CallObjectMethod(j_env, exc, mid_toString)
|
||||
pystr = None if msg_obj == NULL else convert_jobject_to_python(j_env, <bytes> 'Ljava/lang/String;', msg_obj)
|
||||
# If this is not the top-of-the-trace then this is a cause.
|
||||
if len(pystack) > 0:
|
||||
pystack.append("Caused by:")
|
||||
pystack.append(pystr)
|
||||
j_env[0].DeleteLocalRef(j_env, msg_obj)
|
||||
|
||||
# Append stack trace messages if there are any.
|
||||
if frames_length > 0:
|
||||
for i in range(frames_length):
|
||||
# Get the string returned from the 'toString()' method of the next frame and append it to the error message.
|
||||
frame = j_env[0].GetObjectArrayElement(j_env, frames, i)
|
||||
msg_obj = j_env[0].CallObjectMethod(j_env, frame, mid_toString)
|
||||
pystr = None if msg_obj == NULL else convert_jobject_to_python(j_env, <bytes> 'Ljava/lang/String;', msg_obj)
|
||||
pystack.append(pystr)
|
||||
j_env[0].DeleteLocalRef(j_env, msg_obj)
|
||||
j_env[0].DeleteLocalRef(j_env, frame)
|
||||
|
||||
# If 'exc' has a cause then append the stack trace messages from the cause.
|
||||
if frames != NULL:
|
||||
cause = j_env[0].CallObjectMethod(j_env, exc, mid_getCause)
|
||||
if cause != NULL:
|
||||
_append_exception_trace_messages(j_env, pystack, cause,
|
||||
mid_getCause, mid_getStackTrace, mid_toString)
|
||||
j_env[0].DeleteLocalRef(j_env, cause)
|
||||
|
||||
j_env[0].DeleteLocalRef(j_env, frames)
|
||||
|
||||
|
||||
cdef dict assignable_from = {}
|
||||
|
|
|
@ -22,6 +22,17 @@ public class BasicsTest {
|
|||
public float methodF() { return 1.23456789f; };
|
||||
public double methodD() { return 1.23456789; };
|
||||
public String methodString() { return new String("helloworld"); }
|
||||
public void methodException(int depth) throws IllegalArgumentException {
|
||||
if (depth == 0) throw new IllegalArgumentException("helloworld");
|
||||
else methodException(depth -1);
|
||||
}
|
||||
public void methodExceptionChained() throws IllegalArgumentException {
|
||||
try {
|
||||
methodException(5);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("helloworld2", e);
|
||||
}
|
||||
}
|
||||
|
||||
static public boolean fieldStaticZ = true;
|
||||
static public byte fieldStaticB = 127;
|
||||
|
|
|
@ -21,3 +21,31 @@ class BadDeclarationTest(unittest.TestCase):
|
|||
Stack = autoclass('java.util.Stack')
|
||||
stack = Stack()
|
||||
self.assertRaises(JavaException, stack.push, 'hello', 'world', 123)
|
||||
|
||||
def test_java_exception_handling(self):
|
||||
Stack = autoclass('java.util.Stack')
|
||||
stack = Stack()
|
||||
try:
|
||||
stack.pop()
|
||||
self.fail("Expected exception to be thrown")
|
||||
except JavaException as je:
|
||||
# print "Got JavaException: " + str(je)
|
||||
# print "Got Exception Class: " + je.classname
|
||||
# print "Got stacktrace: \n" + '\n'.join(je.stacktrace)
|
||||
self.assertEquals("java.util.EmptyStackException", je.classname)
|
||||
|
||||
def test_java_exception_chaining(self):
|
||||
BasicsTest = autoclass('org.jnius.BasicsTest')
|
||||
basics = BasicsTest()
|
||||
try:
|
||||
basics.methodExceptionChained()
|
||||
self.fail("Expected exception to be thrown")
|
||||
except JavaException as je:
|
||||
# print "Got JavaException: " + str(je)
|
||||
# print "Got Exception Class: " + je.classname
|
||||
# print "Got Exception Message: " + je.innermessage
|
||||
# print "Got stacktrace: \n" + '\n'.join(je.stacktrace)
|
||||
self.assertEquals("java.lang.IllegalArgumentException", je.classname)
|
||||
self.assertEquals("helloworld2", je.innermessage)
|
||||
self.assertIn("Caused by:", je.stacktrace)
|
||||
self.assertEquals(11, len(je.stacktrace))
|
Loading…
Reference in New Issue