在 Jython 中全面捕获异常
trap exceptions comprehensively in Jython
到目前为止,这是我尝试捕获 Jython 代码中的所有异常。我发现,最困难的事情是当你从 Java class 覆盖方法时捕获异常:使用下面的 "vigil" 装饰器(它还测试 EDT/Event Despatch 是否线程状态正确)你可以找到抛出代码的第一行......这样你就可以识别方法本身。但不是线。
此外,通过 Python 和 Java 堆栈追溯堆栈帧完全超出了我的范围。显然,似乎有这些层和 "proxy" classes 的层,这无疑是 Jython 机制中不可避免的一部分。如果有比我聪明得多的人对这个问题感兴趣就好了!
注意这是一个如何使用 "vigil" 装饰器的例子:
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 ... 除了 ... 对每个重写的 Java 方法进行顶部和尾部处理,但这有什么乐趣呢?说真的,即使这样做我也找不到抛出异常的那一行...
这里是 Jython 套接字模块中使用的装饰器示例,用于将 Java 异常映射到 Python 异常。我没有仔细阅读你的 vigil
装饰器,因为它做了很多工作,但我把它放在这里以防它可能有帮助:
def raises_java_exception(method_or_function):
"""Maps java socket exceptions to the equivalent python exception.
Also sets _last_error on socket objects so as to support SO_ERROR.
"""
@wraps(method_or_function)
def handle_exception(*args, **kwargs):
is_socket = len(args) > 0 and isinstance(args[0], _realsocket)
try:
try:
return method_or_function(*args, **kwargs)
except java.lang.Exception, jlx:
raise _map_exception(jlx)
except error, e:
if is_socket:
args[0]._last_error = e[0]
raise
else:
if is_socket:
args[0]._last_error = 0
return handle_exception
我们在这里看到的主要是我们正在调度它是否是 Java 异常(java.lang.Exception
)。我想这可以概括为 java.lang.Throwable
,尽管在任何情况下都不清楚在 java.lang.Error
的情况下可以做什么。肯定没有对应套接字错误!
上面的装饰器依次使用_map_exception
函数来解包Java异常。如您所见,它在此处非常适合特定应用程序:
def _map_exception(java_exception):
if isinstance(java_exception, NettyChannelException):
java_exception = java_exception.cause # unwrap
if isinstance(java_exception, SSLException) or isinstance(java_exception, CertificateException):
cause = java_exception.cause
if cause:
msg = "%s (%s)" % (java_exception.message, cause)
else:
msg = java_exception.message
py_exception = SSLError(SSL_ERROR_SSL, msg)
else:
mapped_exception = _exception_map.get(java_exception.__class__)
if mapped_exception:
py_exception = mapped_exception(java_exception)
else:
py_exception = error(-1, 'Unmapped exception: %s' % java_exception)
py_exception.java_exception = java_exception
return _add_exception_attrs(py_exception)
代码中有些笨拙,我确信还有改进的余地,但总的来说,它确实使任何经过修饰的代码更容易理解。示例:
@raises_java_exception
def gethostname():
return str(InetAddress.getLocalHost().getHostName())
Jim Baker 的回答很有趣...但我想要的是全面的,它记录了引发任何类型的异常时的最大可能数量的堆栈跟踪信息。 CPython 不是多线程,它的堆栈跟踪不必处理 Runnables。我还不够 Jython/Python 专家知道你是否总能在 "pure Python" 代码中获得整个堆栈(即不使用 Java classes)。
但我想要得到的其中一件事是 activity,它导致了 Jython 中 Runnable 的 运行ning。导致 Runnable 的 activity 具有 运行 那个 Runnable 等,直接回到第一个线程。我下面的解决方案,从 Jim 的回答和 doublep 的评论中获得灵感,创建了一个新的 Jython class,TraceableRunnable,它将在创建时存储堆栈跟踪列表。
当引发 Java 或 Python 样式的异常时,这会将所有内容记录回 运行 的开头(如果您系统地使用 TraceableRunnable 而不是 Runnable ).
TraceableRunner subclass 的每个 运行() 代码也必须在某个时候执行此调用:
self.record_thread()
...希望这不是太令人讨厌的强加...
(注意,在真正的 "grown up" 实现中,您想要检查是否已进行此调用...我确信这可以通过一些适当复杂的 Pythonic技术,通过单元测试或其他方式失败。此外,您可能需要在 运行() 代码的最后调用以删除此字典条目...)
这是捕获和记录代码:
class TraceableRunnable( java.lang.Runnable ):
thread_to_tr_dic = {}
def __init__( self ):
# no need to call super's __init__: Runnable is a Java *interface*
caller_thread = java.lang.Thread.currentThread()
self.frame_stack_list = []
if hasattr( caller_thread, 'frame_stack_list' ):
self.frame_stack_list = copy.deepcopy( caller_thread.frame_stack_list )
self.frame_stack_list.append( traceback.extract_stack() )
def record_thread( self ):
TraceableRunnable.thread_to_tr_dic[ java.lang.Thread.currentThread() ] = self
class EDTException( Exception ):
pass
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 exceptions
"""
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 process_exception( exc, python = True ):
tb_obj = sys.exc_info()[ 2 ]
msg = "Exception thrown message %s\nfamily %s, type: %s\n" % ( str( exc ), "Python" if python else "Java", type( exc ))
msg += "traceback object part:\n"
ex_tb = traceback.extract_tb( tb_obj )
# first is frame in vigil
ex_tb = ex_tb[ 1 : ]
if not ex_tb:
msg += " none\n"
else:
tb_strings = traceback.format_list( ex_tb )
for tb_string in tb_strings:
msg += tb_string
curr_thread = java.lang.Thread.currentThread()
if curr_thread in TraceableRunnable.thread_to_tr_dic:
runnable = TraceableRunnable.thread_to_tr_dic[ curr_thread ]
# duck-typing, obviously... although redundant test, as only TraceableRunnables should be in the dictionary...
if hasattr( runnable, 'frame_stack_list' ):
msg += "\nOLDER STACKS:\n"
for frame_stack in runnable.frame_stack_list:
msg += "\nframe stack id: %d\n" % id( frame_stack )
frame_stack = frame_stack[ : -1 ]
if not frame_stack:
msg += " no frames\n"
else:
# most recent call first: reverse array...
stack_strings = traceback.format_list( reversed( frame_stack ))
for stack_string in stack_strings:
msg += stack_string
logger.error( msg )
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:
if req_edt is not None and javax.swing.SwingUtilities.isEventDispatchThread() != req_edt:
msg = \
"vigil: wrong EDT value, should be %s\nfile %s\nline no %s, function: %s" % \
( "EDT" if req_edt else "non-EDT", decorator_file, decorator_line_no, function )
raise EDTException( msg )
return function( *args, **kvargs )
except BaseException, e:
# we cannot know how calling code will want to deal with an EDTException
if type( e ) is EDTException:
raise e
process_exception( e )
except java.lang.Throwable, t:
process_exception( t, False )
return wrapper
return real_decorator
欢迎适当的程序员改进...!
到目前为止,这是我尝试捕获 Jython 代码中的所有异常。我发现,最困难的事情是当你从 Java class 覆盖方法时捕获异常:使用下面的 "vigil" 装饰器(它还测试 EDT/Event Despatch 是否线程状态正确)你可以找到抛出代码的第一行......这样你就可以识别方法本身。但不是线。
此外,通过 Python 和 Java 堆栈追溯堆栈帧完全超出了我的范围。显然,似乎有这些层和 "proxy" classes 的层,这无疑是 Jython 机制中不可避免的一部分。如果有比我聪明得多的人对这个问题感兴趣就好了!
注意这是一个如何使用 "vigil" 装饰器的例子:
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 ... 除了 ... 对每个重写的 Java 方法进行顶部和尾部处理,但这有什么乐趣呢?说真的,即使这样做我也找不到抛出异常的那一行...
这里是 Jython 套接字模块中使用的装饰器示例,用于将 Java 异常映射到 Python 异常。我没有仔细阅读你的 vigil
装饰器,因为它做了很多工作,但我把它放在这里以防它可能有帮助:
def raises_java_exception(method_or_function):
"""Maps java socket exceptions to the equivalent python exception.
Also sets _last_error on socket objects so as to support SO_ERROR.
"""
@wraps(method_or_function)
def handle_exception(*args, **kwargs):
is_socket = len(args) > 0 and isinstance(args[0], _realsocket)
try:
try:
return method_or_function(*args, **kwargs)
except java.lang.Exception, jlx:
raise _map_exception(jlx)
except error, e:
if is_socket:
args[0]._last_error = e[0]
raise
else:
if is_socket:
args[0]._last_error = 0
return handle_exception
我们在这里看到的主要是我们正在调度它是否是 Java 异常(java.lang.Exception
)。我想这可以概括为 java.lang.Throwable
,尽管在任何情况下都不清楚在 java.lang.Error
的情况下可以做什么。肯定没有对应套接字错误!
上面的装饰器依次使用_map_exception
函数来解包Java异常。如您所见,它在此处非常适合特定应用程序:
def _map_exception(java_exception):
if isinstance(java_exception, NettyChannelException):
java_exception = java_exception.cause # unwrap
if isinstance(java_exception, SSLException) or isinstance(java_exception, CertificateException):
cause = java_exception.cause
if cause:
msg = "%s (%s)" % (java_exception.message, cause)
else:
msg = java_exception.message
py_exception = SSLError(SSL_ERROR_SSL, msg)
else:
mapped_exception = _exception_map.get(java_exception.__class__)
if mapped_exception:
py_exception = mapped_exception(java_exception)
else:
py_exception = error(-1, 'Unmapped exception: %s' % java_exception)
py_exception.java_exception = java_exception
return _add_exception_attrs(py_exception)
代码中有些笨拙,我确信还有改进的余地,但总的来说,它确实使任何经过修饰的代码更容易理解。示例:
@raises_java_exception
def gethostname():
return str(InetAddress.getLocalHost().getHostName())
Jim Baker 的回答很有趣...但我想要的是全面的,它记录了引发任何类型的异常时的最大可能数量的堆栈跟踪信息。 CPython 不是多线程,它的堆栈跟踪不必处理 Runnables。我还不够 Jython/Python 专家知道你是否总能在 "pure Python" 代码中获得整个堆栈(即不使用 Java classes)。
但我想要得到的其中一件事是 activity,它导致了 Jython 中 Runnable 的 运行ning。导致 Runnable 的 activity 具有 运行 那个 Runnable 等,直接回到第一个线程。我下面的解决方案,从 Jim 的回答和 doublep 的评论中获得灵感,创建了一个新的 Jython class,TraceableRunnable,它将在创建时存储堆栈跟踪列表。
当引发 Java 或 Python 样式的异常时,这会将所有内容记录回 运行 的开头(如果您系统地使用 TraceableRunnable 而不是 Runnable ).
TraceableRunner subclass 的每个 运行() 代码也必须在某个时候执行此调用:
self.record_thread()
...希望这不是太令人讨厌的强加...
(注意,在真正的 "grown up" 实现中,您想要检查是否已进行此调用...我确信这可以通过一些适当复杂的 Pythonic技术,通过单元测试或其他方式失败。此外,您可能需要在 运行() 代码的最后调用以删除此字典条目...)
这是捕获和记录代码:
class TraceableRunnable( java.lang.Runnable ):
thread_to_tr_dic = {}
def __init__( self ):
# no need to call super's __init__: Runnable is a Java *interface*
caller_thread = java.lang.Thread.currentThread()
self.frame_stack_list = []
if hasattr( caller_thread, 'frame_stack_list' ):
self.frame_stack_list = copy.deepcopy( caller_thread.frame_stack_list )
self.frame_stack_list.append( traceback.extract_stack() )
def record_thread( self ):
TraceableRunnable.thread_to_tr_dic[ java.lang.Thread.currentThread() ] = self
class EDTException( Exception ):
pass
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 exceptions
"""
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 process_exception( exc, python = True ):
tb_obj = sys.exc_info()[ 2 ]
msg = "Exception thrown message %s\nfamily %s, type: %s\n" % ( str( exc ), "Python" if python else "Java", type( exc ))
msg += "traceback object part:\n"
ex_tb = traceback.extract_tb( tb_obj )
# first is frame in vigil
ex_tb = ex_tb[ 1 : ]
if not ex_tb:
msg += " none\n"
else:
tb_strings = traceback.format_list( ex_tb )
for tb_string in tb_strings:
msg += tb_string
curr_thread = java.lang.Thread.currentThread()
if curr_thread in TraceableRunnable.thread_to_tr_dic:
runnable = TraceableRunnable.thread_to_tr_dic[ curr_thread ]
# duck-typing, obviously... although redundant test, as only TraceableRunnables should be in the dictionary...
if hasattr( runnable, 'frame_stack_list' ):
msg += "\nOLDER STACKS:\n"
for frame_stack in runnable.frame_stack_list:
msg += "\nframe stack id: %d\n" % id( frame_stack )
frame_stack = frame_stack[ : -1 ]
if not frame_stack:
msg += " no frames\n"
else:
# most recent call first: reverse array...
stack_strings = traceback.format_list( reversed( frame_stack ))
for stack_string in stack_strings:
msg += stack_string
logger.error( msg )
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:
if req_edt is not None and javax.swing.SwingUtilities.isEventDispatchThread() != req_edt:
msg = \
"vigil: wrong EDT value, should be %s\nfile %s\nline no %s, function: %s" % \
( "EDT" if req_edt else "non-EDT", decorator_file, decorator_line_no, function )
raise EDTException( msg )
return function( *args, **kvargs )
except BaseException, e:
# we cannot know how calling code will want to deal with an EDTException
if type( e ) is EDTException:
raise e
process_exception( e )
except java.lang.Throwable, t:
process_exception( t, False )
return wrapper
return real_decorator
欢迎适当的程序员改进...!