在Jython中全面地捕获异常 [英] trap exceptions comprehensively in Jython

查看:244
本文介绍了在Jython中全面地捕获异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是迄今为止我试图捕获Jython代码中的所有异常。我发现最困难的事情是在从Java类中重写方法时捕获异常:使用下面的vigil装饰器(也可以测试EDT / Event Despatch Thread的状态是否正确),您可以找到第一行在那里代码被抛出...所以你可以识别方法本身。但不是这一行。



此外,通过Python和Java堆栈跟踪堆栈框架完全超出了我。显然似乎有这些层次和层次的代理类,这无疑是Jython力学的一部分。如果有一个比我更聪明的人对这个问题感兴趣,那将是很棒的!



注意这是一个如何使用守夜装饰器的例子:

  class ColumnCellRenderer(javax.swing.table.DefaultTableCellRenderer):
@vigil(True)#表示检查完成线程是EDT,以及拦截Python异常和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):

捕获Python-使用命令sys.excepthook = custom_hook创建样式BaseExceptions。
这个方法在Jython中需要做的第一件事是确定这是否是一个Java
java.lang.Throwable与否。如果是这样JThrowable
必须由适用于B {未捕获的Java Throwables}的代码来处理。

try:

如果'tb'不在本地()中:
tb =无
logger.error(Python custom_hook call ... \\\
type of e:%s\\\
e:%s\\\
tb:%s%(unicode(type_of_e),unicode(e),
unicode(tb)))
msg =''.join(traceback.format_exception(type_of_e,e,tb))
logger.error(traceback:\\\
+ msg)
除了BaseException,e:
logger .Err .UncaughtExceptionHandler):

java.lang.Class来捕获应用程序抛出的任何Java Throwables。

def uncaughtException(self,thread,throwable):
try:
'''
NB得到这样的Java堆栈跟踪似乎产生非常不同的trace
from throwable.printStackTrace()...为什么?
''
#我们要一个单一的日志消息
exception_msg =\\\
*** uncaught Java异常被记录在%s中:\\\
%__file__
baos = java.io.ByteArrayOutputStream()
ps = java.io.PrintStream(baos)
throwable.printStackTrace(ps )
#从Java堆栈跟踪消息中删除多行
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 ='\\\
'.join(java_stack_trace_lines)
exception_msg + = normalised_java_stack_trac e +'\\\
'
python_traceback_string = traceback.format_exc()
exception_msg + =Python traceback:\\\
%s%python_traceback_string
logger.error(exception_msg)
except(BaseException,java.lang.Throwable),e:
logger.error(*** exception in Java exception handler:\\\
type%s\\\
%s%(type(e) unicode(e)))
raise e
#NB printStackTrace导致custom_hook被调用...(但不打印任何东西)
java.lang.Thread.setDefaultUncaughtExceptionHandler(JavaUncaughtExceptHandler( )


def vigil(* args):

具有两个功能的装饰器。
1.检查在EDT或非EDT线程中运行的方法;
2.捕获任何Java Throwables,否则将无法正确捕获和记录:特别是
与Jython中正常的Java错误捕获似乎不可能确定一个$ b $的行号b异常被抛出。这至少记录了Java java.lang.Throwable
被抛出的行。

如果len(args)!= 1:
raise异常(守夜:错误的args数(应为1,值:无/ True / False):%s %str(args))
req_edt = args [0]
如果req_edt和类型(req_edt)不是bool:
raise异常(vigil:edt_status是错误的类型:%s,类型%s%(req_edt,type(req_edt)))
def real_decorator(function):
如果不是hasattr(function,'__call__'):
raise异常(vigil:function %s没有__call__ attr,键入%s
%(函数,类型(函数)))

#NB关于装饰器位置的信息在打包器调用时无法获得,所以记录在这一点
penultimate_frame = traceback.extract_stack()[-2]
decorator_file = penultimate_frame [0]
decorator_line_no = penultimate_frame [1]
def wrapper(* args, ** kvargs):
try:
#TODO是po此时可以获取Python和/或Java堆栈跟踪?
如果req_edt和javax.swing.SwingUtilities.isEventDispatchThread()!= req_edt:
logger.error(*** vigil:错误的EDT值,应为%s\\\
file%s,行no %s,函数:%s\\\

(EDTif req_edt elsenon-EDT,decorator_file,decorator_line_no,function))
return function(* args,** kvargs)
except(BaseException,java.lang.Throwable),e:
'''NB如果保护功能的函数引发异常,则会出现各种问题:
1)只是提高e意味着你得到一个非常短的堆栈跟踪...
2)如果列出堆栈跟踪元素在这里你得到一行(看起来在
异常发生的函数内),但往往是错误的行!
3)Python / Java堆栈框架:这一切如何工作?
4)想要单个错误消息被记录
'''
msg =***异常%s被文件%s\\\
in函数起始行%d %(e,decorator_file,decorator_line_no)
logger.error(msg)
frame = inspect.currentframe()
#以下似乎不起作用?
python_stack_trace = traceback.format_stack(frame)
python_stack_string =Python堆栈跟踪:\\\

在python_stack_trace [:-1]中:
python_stack_string + = el
logger.error(python_stack_string)
如果isinstance(e,java.lang.Throwable):
#这个堆栈跟踪的NB问题:虽然它似乎显示
#正确的Java调用路径,似乎每个文件和方法
#的行号始终显示为最后一行,无论实际提出了异常。
#可能尝试抓住实际的Pyxxx对象...(?)
java_stack_trace = e.stackTrace
java_stack_string =Java堆栈跟踪:\\\

el in java_stack_trace:
java_stack_string + =%s\\\
%unicode(el)
logger.error(java_stack_string)
raise e
return wrapper
return当然可以使用try来顶替和覆盖每个被覆盖的Java方法。除了...但是那里的乐趣在哪里?真的,即使这样做,我找不到异常抛出的行...

解决方案

吉姆·贝克的回答有趣...但是我想要的是全面的,记录了任何类型的异常提出时堆栈跟踪信息的最大可能量。 CPython不是多线程,其堆栈跟踪不必处理Runnables。我不够一个Jython / Python专家,不知道你是否能够总是以纯Python代码(即不使用Java类)获得整个堆栈。



但是我想要得到的一件事是导致在Jython中运行Runnable的活动。并且导致Runnable已经运行了Runnable等的活动回到了第一个线程。下面我的解决方案来自Jim的答案,也来自于doublep的评论,创建了一个新的Jython类TraceableRunnable,它将在创建时存储堆栈跟踪列表。



一个例外,Java或Python风格,被提出,这将记录所有的事情,直到运行的开始(如果您系统地使用TraceableRunnable而不是Runnable)。



每个一个TraceableRunner子类的run()代码也必须在某个时候执行此调用:

  self.record_thread()

...希望这不是太令人讨厌的...



(在一个真正的成年实施中,你想要检查这个电话是做了...我确信这可以通过一些适当复杂的Pythonic技术完成,失败的单位测试或某些东西,还可能需要在run()代码的最后一个电话来删除这个字典条目...)



这是捕捉和记录代码:

  class TraceableRunnable(java.lang.Runnable):
thread_to_tr_dic = {}
def __init __(self):
#不需要调用super的__init__:Runnable是一个Java *接口*
caller_thread = java.lang.Thread.currentThread()
self.frame_stack_list = []
如果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 (例外):
pass

def vigil(* args):

具有两个功能的装饰器。
1.检查在EDT或非EDT线程中运行的方法
2.捕获任何异常

if len(args)! = 1:
raise异常(vigil:args的数量(应为1,值:无/ True / False):%s%str(args))
req_edt = args [0]
如果req_edt和类型(req_edt)不是bool:
raise异常(vigil:edt_status是错误的类型:%s,类型%s%(req_edt,type(req_edt)))


def process_exception(exc,python = True):
tb_obj = sys.exc_info()[2]
msg =异常抛出的消息%s\\\
family%s ,键入:%s\\\
%(str(exc),Pythonif python elseJava,type(exc))
msg + =traceback object part:\\\

ex_tb = traceback.extract_tb(tb_obj)
#first is frame in vigil
ex_tb = ex_tb [1:]
如果不是ex_tb:
msg + =none\\\

else:
tb_str ings = traceback.format_list(ex_tb)
在tb_strings中的tb_string
msg + = tb_string

curr_thread = java.lang.Thread.currentThread()
如果curr_thread在TraceableRunnable.thread_to_tr_dic中:
runnable = TraceableRunnable.thread_to_tr_dic [curr_thread]
#duck-typing,显然...虽然冗余测试,只有TraceableRunnables应该在字典...
if hasattr(runnable,'frame_stack_list'):
msg + =\\\
OLDER STACKS:\\\

for frame_stack in runnable.frame_stack_list:
msg + =\\\
frame stack id :%d\\\
%id(frame_stack)
frame_stack = frame_stack [:-1]
如果不是frame_stack:
msg + =no frames\\\

其他:
#最近的电话第一:反向数组...
stack_strings = traceback.format_list(inverted(frame_stack))
在stack_strings中的stack_string:
msg + = stack_string
logger.error(msg)


def real_decorator(function):
如果不是hasattr(function,'__call__'):
raise异常(vigil:function%s没有__call__ attr,键入%s
%(函数,类型(函数)))
#注意在包装器调用时无法获取有关装饰器位置的信息,因此在此处记录
penultimate_frame = traceback.extract_stack( )[-2]
decorator_file = penultimate_frame [0]
decorator_line_no = penultimate_frame [1]
def wrapper(* args,** kvargs):
try:
如果req_edt不是None和javax.swing.SwingUtilities.isEventDispatchThread()!= req_edt:
msg = \
守夜:错误的EDT值,应为%s\\\
file%s\\\
line no%s,function:%s%\
(EDTif req_edt else 非EDT,decorator_file,decorator_line_no,函数)
raise EDTException(msg)
返回函数(* args,** kvargs)
除了BaseException,e:
#不知道调用代码如何处理EDTException
如果type(e)是EDTException:
raise e
process_exception(e)
除了java.lang.Throwable,t :
process_exception(t,False)
return wrapper
return real_decorator

欢迎来自正确程序员的改进!!


This is my attempt so far to trap all exceptions in Jython code. The most difficult thing, I find, is trapping exceptions when you override a method from a Java class: with the "vigil" decorator below (which also tests whether the EDT/Event Despatch Thread status is correct) you can find out the first line where the code is thrown... so you can identify the method itself. But not the line.

Furthermore, tracing stack frames back through Python and Java stacks is completely beyond me. Obviously there seem to be these layers and layers of "proxy" classes, a no doubt inevitable part of Jython mechanics. It'd be great if someone much cleverer than me were to take an interest in this question!

NB this is an example of how to use the "vigil" decorator:

    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
            ...

... and these are three functions I use in an attempt to trap stuff...

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 it is of course possible to top-and-tail every overridden Java method with try ... except... but where's the fun in that? Seriously, even doing that I'm unable to find the line at which the exception is thrown...

解决方案

Jim Baker's answer is interesting... but what I wanted is sthg comprehensive which documents the maximum possible amount of stack trace info when an exception of any kind is raised. CPython not being multi-thread, its stack trace doesn't have to cope with Runnables. I'm not enough of a Jython/Python expert to know whether you can always get the whole stack in "pure Python" code (i.e. no use of Java classes).

But one of the things I wanted to get was the activity which had led up to the running of a Runnable in Jython. And the activity which had led up to the Runnable having run that Runnable, etc., right back to the very first thread. My solution below, taking inspiration from Jim's answer but also from doublep's comment, creates a new Jython class, TraceableRunnable, which will store the list of stack traces on creation.

When an exception, either Java or Python style, is raised, this logs everything right back to the start of the run (if you systematically use TraceableRunnable instead of Runnable).

Each run() code of a TraceableRunner subclass also has to do this call at some point:

self.record_thread()

... hopefully this is not too irksome an imposition...

(NB in a truly "grown up" implementation you'd want to check that this call had been made... I'm sure that this could be done by some suitably sophisticated Pythonic technique, failing which by unit testing or something. Also you might want to require a call at the very end of the run() code to delete this dictionary entry...)

This is the catching and logging code:

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

Would welcome improvements from proper programmers...!

这篇关于在Jython中全面地捕获异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆