Как поймать исключение в Python и получить ссылку на исключение, не зная тип? - PullRequest
19 голосов
/ 12 января 2010

Мне интересно, как я могу поймать любой поднятый объект (т. Е. Тип, который не расширяется Exception), и все еще получать ссылку на него.

Я натолкнулся на желание сделать это при использовании Jython. При вызове метода Java, если этот метод вызывает исключение, он не будет расширять класс Exception Python, поэтому такой блок не будет его перехватывать:

try:
    # some call to a java lib that raises an exception here
except Exception, e:
    # will never be entered

Я могу это сделать, но у меня нет доступа к объекту исключения, который был вызван.

try:
    # some call to a java lib that raises an exception here
except:
    # will enter here, but there's no reference to the exception that was raised

Я могу решить эту проблему, импортировав тип исключения Java и перехватив его явно, но это затрудняет / делает невозможным написание общих обработчиков / декораторов обработки исключений.

Есть ли способ перехватить какое-то произвольное исключение и все же получить ссылку на него в блоке except?

Я должен отметить , что я надеюсь, что декоратор обработки исключений, который я создаю, пригоден для использования с проектами Python, а не только с проектами Jython. Я хотел бы избежать импорта java.lang.Exception, потому что это делает его только Jython. Например, я полагаю, что могу сделать что-то подобное (но я не пробовал), но я бы хотел этого избежать, если смогу.

try:
    # some function that may be running jython and may raise a java exception
except (Exception, java.lang.Exception), e:
    # I imagine this would work, but it makes the code jython-only

Ответы [ 3 ]

35 голосов
/ 12 января 2010

Вы можете ссылаться на исключения, используя модуль sys. sys.exc_info - это кортеж типа, экземпляра и трассировки.

import sys

try:
    # some call to a java lib that raises an exception here
except:
    instance = sys.exc_info()[1]
9 голосов
/ 17 декабря 2013

FWIW, я обнаружил, что если вы добавите этот импорт в свой скрипт Jython:

from java.lang import Exception

и просто используйте обычный обработчик исключений Python:

except Exception, e:

будет перехватывать и Python-исключения и Java-исключения

5 голосов
/ 03 июля 2013

Просто для всех, кто интересуется ... Я потратил немного времени на тестирование, потому что хотел выяснить, как получить правильную трассировку стека, будь то исключение Python (на самом деле BaseException, который является базовым классом) или Java. lang.Throwable (базовый класс Java для Exception, Error и т. д.) выброшен ... этот код иллюстрирует, как правильно перехватить все ссылки на номера строк.

import sys
import traceback
import java

print "hello world"

def custom_hook( type, exc, tb ):
  if isinstance( sys.exc_info()[ 1 ], java.lang.Throwable ):
    sys.stderr.write( "AS JAVA:\n" )
    sys.exc_info()[ 1 ].printStackTrace() # java part
  else:
    sys.stderr.write( "NO JAVA TRACE:\n" )
  sys.stderr.write( "AS PYTHON:\n" )
  traceback.print_exc()

# useful for custom exception handling!
sys.excepthook = custom_hook  

def handle_exc():
# generate either a java.lang.Throwable (uncomment the next line and comment out "a = 16 / 0"
#  java.lang.String( None )
# OR... a python-style BaseException:
  a = 16 / 0 

class Task( java.lang.Runnable ):
  def run( self ):
    # NB the problem with all this stuff is that the Java stack trace shows
    # a java.lang.Throwable occurring at the last line of this code block...
#    print "lots of stuff first"
#    print "lots 2"
#    handle_exc()
#    print "lots 3"
#    print "lots of stuff after"

    try:
      print "lots of stuff first"
      print "lots 2"
      handle_exc()
      print "lots 3"
      print "lots of stuff after"
    # NB do not catch both (Python) BaseException and java.lang.Throwable...   
#    except ( BaseException, java.lang.Throwable ), e:
    # the above means that the line no. in handle_exc is not shown when a BaseException  
    # is thrown...
    except java.lang.Throwable, t:
      tb = sys.exc_info()[ 2 ] 
      sys.stderr.write( "java.lang.Throwable thrown at: %s\n" % tb.tb_lineno )
      raise t

java.awt.EventQueue.invokeAndWait( Task() )

После этого можно подумать о написании декоратора, предшествующего def run (self) и аналогичным методам, чтобы вам не приходилось каждый раз выписывать этот блок catch-the-Throwable try-Кроме ... в частности:

def throw_trap( function ):
  def wrapper(*args, **kvargs):
    try:
      return function( *args, **kvargs )
    except  java.lang.Throwable, t:
      tb = sys.exc_info()[ 2 ]
      while( tb ): 
        sys.stderr.write( "thrown at: %s\n" % tb.tb_lineno )
        tb = tb.tb_next
      raise t
  return wrapper



def handle_exc():
  java.lang.String( None )
#  a = 16 / 0 


class Task( java.lang.Runnable ):
  @throw_trap
  def run( self ):
    print "lots of stuff first"
    print "lots 2"
    handle_exc()
    print "lots 3"
    print "lots of stuff after"

java.awt.EventQueue.invokeAndWait( Task() )
...