這是我迄今爲止嘗試在Jython代碼中捕獲所有異常的嘗試。我發現,最困難的事情是在從Java類中覆蓋方法時捕獲異常:使用下面的「守夜」修飾器(它也測試EDT/Event Despatch Thread狀態是否正確),您可以找到第一行代碼被拋出的地方...所以你可以確定方法本身。但不是線路。在Jython中全面捕捉異常
此外,通過Python和Java堆棧追溯堆棧幀完全超出了我。顯然,似乎有這些「代理」類的層和層,毫無疑問,Jython機制是不可避免的一部分。如果有人比我更聰明地對這個問題感興趣,那會很棒!
NB這是一個如何使用「守夜」裝飾爲例:
......這些都是三個功能我試圖捕獲的東西用...
def custom_hook(type_of_e, e, tb):
"""
Method to catch Python-style BaseExceptions, using the command sys.excepthook = custom_hook.
The first thing this method needs to do in Jython is to determine whether this is a Java
java.lang.Throwable or not. If it is this JThrowable
must be handled by the code which caters for B{uncaught Java Throwables}.
"""
try:
if 'tb' not in locals():
tb = None
logger.error("Python custom_hook called...\ntype of e: %s\ne: %s\ntb: %s" % (unicode(type_of_e), unicode(e),
unicode(tb)))
msg = ''.join(traceback.format_exception(type_of_e, e, tb))
logger.error("traceback:\n" + msg)
except BaseException, e:
logger.error("exception in Python custom_hook!:\n%s" % e)
raise e
sys.excepthook = custom_hook
class JavaUncaughtExceptHandler(java.lang.Thread.UncaughtExceptionHandler):
"""
java.lang.Class to catch any Java Throwables thrown by the app.
"""
def uncaughtException(self, thread, throwable):
try:
'''
NB getting the Java stack trace like this seems to produce a very different trace
from throwable.printStackTrace()... why?
'''
# we want a single log message
exception_msg = "\n*** uncaught Java Exception being logged in %s:\n" % __file__
baos = java.io.ByteArrayOutputStream()
ps = java.io.PrintStream(baos)
throwable.printStackTrace(ps)
# remove multiple lines from Java stack trace message
java_stack_trace_lines = unicode(baos.toString("ISO-8859-1")).splitlines()
java_stack_trace_lines = filter(None, java_stack_trace_lines )
normalised_java_stack_trace = '\n'.join(java_stack_trace_lines)
exception_msg += normalised_java_stack_trace + '\n'
python_traceback_string = traceback.format_exc()
exception_msg += "Python traceback:\n%s" % python_traceback_string
logger.error(exception_msg)
except (BaseException, java.lang.Throwable), e:
logger.error("*** exception in Java exception handler:\ntype %s\n%s" % (type(e), unicode(e)))
raise e
# NB printStackTrace causes the custom_hook to be invoked... (but doesn't print anything)
java.lang.Thread.setDefaultUncaughtExceptionHandler(JavaUncaughtExceptHandler() )
def vigil(*args):
"""
Decorator with two functions.
1. to check that a method is being run in the EDT or a non-EDT thread;
2. to catch any Java Throwables which otherwise would not be properly caught and documented: in particular,
with normal Java error-trapping in Jython it seems impossible to determine the line number at which an
Exception was thrown. This at least records the line at which a Java java.lang.Throwable
was thrown.
"""
if len(args) != 1:
raise Exception("vigil: wrong number of args (should be 1, value: None/True/False): %s" % str(args))
req_edt = args[ 0 ]
if req_edt and type(req_edt) is not bool:
raise Exception("vigil: edt_status is wrong type: %s, type %s" % (req_edt, type(req_edt)))
def real_decorator(function):
if not hasattr(function, '__call__'):
raise Exception("vigil: function %s does not have __call__ attr, type %s"
% (function, type(function)))
# NB info about decorator location can't be got when wrapper called, so record it at this point
penultimate_frame = traceback.extract_stack()[ -2 ]
decorator_file = penultimate_frame[ 0 ]
decorator_line_no = penultimate_frame[ 1 ]
def wrapper(*args, **kvargs):
try:
# TODO is it possible to get the Python and/or Java stack trace at this point?
if req_edt and javax.swing.SwingUtilities.isEventDispatchThread() != req_edt:
logger.error("*** vigil: wrong EDT value, should be %s\nfile %s, line no %s, function: %s\n" %
("EDT" if req_edt else "non-EDT", decorator_file, decorator_line_no, function))
return function(*args, **kvargs)
except (BaseException, java.lang.Throwable), e:
''' NB All sorts of problems if a vigil-protected function throws an exception:
1) just raising e means you get a very short stack trace...
2) if you list the stack trace elements here you get a line (seemingly inside the function where the
exception occurred) but often the wrong line!
3) Python/Java stack frames: how the hell does it all work???
4) want a single error message to be logged
'''
msg = "*** exception %s caught by vigil in file %s\nin function starting line %d" % (e, decorator_file, decorator_line_no)
logger.error(msg)
frame = inspect.currentframe()
# the following doesn't seem to work... why not?
python_stack_trace = traceback.format_stack(frame)
python_stack_string = "Python stack trace:\n"
for el in python_stack_trace[ : -1 ]:
python_stack_string += el
logger.error(python_stack_string)
if isinstance(e, java.lang.Throwable):
# NB problems with this stack trace: although it appears to show the
# correct Java calling pathway, it seems that the line number of every file and method
# is always shown as being the last line, wherever the exception was actually raised.
# Possibly try and get hold of the actual Pyxxx objects ... (?)
java_stack_trace = e.stackTrace
java_stack_string = "Java stack trace:\n"
for el in java_stack_trace:
java_stack_string += " %s\n" % unicode(el)
logger.error(java_stack_string)
raise e
return wrapper
return real_decorator
PS當然可以通過try ... top-and-tail重寫每一個Java方法...除了...但是在哪裏有趣呢?嚴重的是,即使這樣做,我無法找到排除異常的線......
使用'except:'(即不指定類型)和通過'sys.exc_info()'訪問異常可能會給出更好的結果。 – doublep
嘿,好的提示,謝謝! –