这是我迄今为止在 Jython 代码中捕获所有异常的尝试。我发现,最困难的事情是当您从 Java 类中重写方法时捕获异常:使用下面的“vigil”装饰器(它还测试 EDT/Event Despatch Thread 状态是否正确),您可以找到第一行代码被抛出的地方......所以你可以识别方法本身。但不是线。
此外,通过 Python 和 Java 堆栈追溯堆栈帧完全超出了我的范围。显然,似乎存在一层又一层的“代理”类,这无疑是 Jython 机制不可避免的一部分。如果比我聪明得多的人对这个问题感兴趣,那就太好了!
注意这是一个如何使用“守夜”装饰器的例子:
class ColumnCellRenderer( javax.swing.table.DefaultTableCellRenderer ):
@vigil( True ) # means a check is done that the thread is the EDT, as well as intercepting Python Exceptions and Java Throwables...
def getTableCellRendererComponent( self, table, value, isSelected, hasFocus, row, column):
super_comp = self.super__getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column)
super_comp.foreground = java.awt.Color.black
super_comp.font = main_frame_self.m_plain_font
...
...这是我用来捕获东西的三个功能...
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 ... except ...对每个被覆盖的Java方法进行顶部和尾部,但其中的乐趣在哪里?说真的,即使这样做我也无法找到引发异常的行...