İstisnaya bilgi mi ekliyorsunuz?


144

Böyle bir şey elde etmek istiyorum:

def foo():
   try:
       raise IOError('Stuff ')
   except:
       raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
       e.message = e.message + 'happens at %s' % arg1
       raise

bar('arg1')
Traceback...
  IOError('Stuff Happens at arg1')

Ama ne olsun:

Traceback..
  IOError('Stuff')

Bunu nasıl başaracağınıza dair ipuçları var mı? Python 2 ve 3'te nasıl yapılır?


İstisna messageözniteliği için dokümantasyon ararken bu SO sorusunu buldum , Python 2.6'da kullanımdan vazgeçildiğini gösteren BaseException.message artık kullanılmıyor (ve neden dokümanlarda değil).
martineau

ne yazık ki, bu bağlantı artık çalışmıyor gibi görünüyor.
Michael Scott Cuthbert

1
@MichaelScottCuthbert iyi bir alternatif: itmaybeahack.com/book/python-2.6/html/p02/…
Niels Keurentjes

Burada , message özniteliğinin durumunun ne olduğunu ve bunun args özniteliği ve PEP 352 ile ilişkisinin gerçekten iyi bir açıklaması var . Steven F. Lott'un Python'daki ücretsiz Beceri Oluşturma kitabından .
martineau

Yanıtlar:


119

Bunu böyle yapardım, bu yüzden türünü foo()değiştirmek de değiştirmeyi gerektirmez bar().

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
        foo()
    except Exception as e:
        raise type(e)(e.message + ' happens at %s' % arg1)

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    raise type(e)(e.message + ' happens at %s' % arg1)
IOError: Stuff happens at arg1

Güncelleme 1

Orijinal geri izlemeyi koruyan küçük bir değişiklik:

...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e), type(e)(e.message +
                               ' happens at %s' % arg1), sys.exc_info()[2]

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    foo()
  File "test.py", line 5, in foo
    raise IOError('Stuff')
IOError: Stuff happens at arg1

Güncelleme 2

Python 3.x için, ilk güncellemede kod sözdizimi yanlış artı fikri olan messageüzerinde niteliğini BaseExceptionedildi PEP 352 bir değişiklik geri çekilir (benim ilk güncelleme 2012-03-12 tarihinde yayınlanmıştır) 2012-05-16 tarihinde . Yani, şu anda, Python 3.5.2'de, izlemeyi korumak ve işlevdeki istisna türünü sabit kodlamak için bu çizgiler boyunca bir şeyler yapmanız gerekir bar(). Ayrıca satır olacağını unutmayın:

During handling of the above exception, another exception occurred:

görüntülenen geri izleme mesajlarında.

# for Python 3.x
...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e)(str(e) +
                      ' happens at %s' % arg1).with_traceback(sys.exc_info()[2])

bar('arg1')

Güncelleme 3

Cevap nedeniyle sözdizimi farklılıklara "Hayır" olarak gözükse de Python 2 ve 3'de hem de çalışacak bir yolu olsaydı bir yorumcu istedi, orada olduğu gibi bir yardımcı işlevi kullanarak o etrafında bir yolu reraise()içinde sixAdd-On'un modül üzerinde. Bu nedenle, kitaplığı herhangi bir nedenle kullanmak istemiyorsanız, aşağıda basitleştirilmiş bağımsız bir sürüm bulunmaktadır.

İstisna, işlev içinde yeniden düzenlendiği için reraise(), bu izin kaldırıldığı yerlerde görünecektir, ancak nihai sonuç istediğiniz şeydir.

import sys

if sys.version_info.major < 3:  # Python 2?
    # Using exec avoids a SyntaxError in Python 3.
    exec("""def reraise(exc_type, exc_value, exc_traceback=None):
                raise exc_type, exc_value, exc_traceback""")
else:
    def reraise(exc_type, exc_value, exc_traceback=None):
        if exc_value is None:
            exc_value = exc_type()
        if exc_value.__traceback__ is not exc_traceback:
            raise exc_value.with_traceback(exc_traceback)
        raise exc_value

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
        reraise(type(e), type(e)(str(e) +
                                 ' happens at %s' % arg1), sys.exc_info()[2])

bar('arg1')

3
Bu, mevcut bir istisnaya bilgi ekleme noktasını yenerek geri izini kaybeder. Ayrıca,> 1 argüman alan ctor ile istisnalar işe yaramaz (tür, istisnayı yakaladığınız yerden kontrol edemediğiniz bir şeydir).
Václav Slavík

1
@ Václav: Eklediğim güncellemede gösterildiği gibi, geri izi kaybetmeyi önlemek oldukça kolay. Bu hala akla gelebilecek her istisnayı ele almasa da, OP'nin sorusunda gösterilene benzer durumlar için işe yarar.
martineau

1
Bu pek doğru değil. (E) türü geçersiz kılınırsa __str__, istenmeyen sonuçlar alabilirsiniz. Ayrıca ikinci argümanın, birinci argüman tarafından verilen yapıcıya aktarıldığını ve bu da biraz saçma bir sonuç verdiğini unutmayın type(e)(type(e)(e.message). Üçüncüsü, e.message e.args [0] lehine kullanımdan kaldırılmıştır .
bukzor

1
Python 2 ve 3'te çalışan taşınabilir bir yol yok mu?
Elias Dorneles

1
@martineau Dıştaki bloğa içe aktarmanın amacı nedir? Bu sadece gerektiğinde içe aktararak bellek tasarrufu yapmak için mi?
AllTradesJack

117

Buraya Python 3 için bir çözüm aramak için geldiyseniz , el kitabı şöyle diyor:

Yeni bir istisna yükseltirken ( raiseşu anda işlenmekte olan istisnayı yeniden yükseltmek için bir çıplak kullanmak yerine ), örtülü istisna bağlamı zam ile birlikte kullanılarak açık bir nedenle desteklenebilir:

raise new_exc from original_exc

Misal:

try:
    return [permission() for permission in self.permission_classes]
except TypeError as e:
    raise TypeError("Make sure your view's 'permission_classes' are iterable. "
                    "If you use '()' to generate a set with a single element "
                    "make sure that there is a comma behind the one (element,).") from e

Sonunda şöyle görünür:

2017-09-06 16:50:14,797 [ERROR] django.request: Internal Server Error: /v1/sendEmail/
Traceback (most recent call last):
File "venv/lib/python3.4/site-packages/rest_framework/views.py", line 275, in get_permissions
    return [permission() for permission in self.permission_classes]
TypeError: 'type' object is not iterable 

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
    # Traceback removed...
TypeError: Make sure your view's Permission_classes are iterable. If 
     you use parens () to generate a set with a single element make 
     sure that there is a (comma,) behind the one element.

TypeErrorOrijinal İstisna'yı bozmadan bir çözüme yönelik ipuçlarıyla tamamen sıradan olmayan güzel bir mesaja dönüştürmek.


14
Bu en iyi çözümdür, çünkü ortaya çıkan istisna orijinal nedeni işaret eder, daha fazla ayrıntı sağlar.
JT

mesaj ekleyebileceğimiz ancak yeni bir istisna getiremediğimiz herhangi bir çözüm var mı? Yani istisna örneğinin mesajını uzatmak.
edcSam

Yaa ~~ Çalışıyor, ama bana yapılmaması gereken bir şey gibi geliyor. İleti depolanır e.args, ancak bu bir Tuple'dir, bu nedenle değiştirilemez. Bu yüzden önce argsbir listeye kopyalayın , sonra değiştirin, ardından bir Tuple olarak kopyalayın:args = list(e.args) args[0] = 'bar' e.args = tuple(args)
Chris

27

Foo () işlevini değiştirmek istemediğinizi veya değiştiremeyeceğinizi varsayarsak, bunu yapabilirsiniz:

try:
    raise IOError('stuff')
except Exception as e:
    if len(e.args) >= 1:
        e.args = (e.args[0] + ' happens',) + e.args[1:]
    raise

Bu, Python 3'teki sorunu çirkin ve kafa karıştırıcı olmadan çözen tek çözümdür. "Yukarıdaki istisnanın işlenmesi sırasında başka bir istisna oluştu" mesajı.

Yeniden yükseltme çizgisinin yığın izlemesine eklenmesi durumunda, hile yapmak raise eyerine yazmak gerekir raise.


ama bu durumda istisna foo değişirse, ben de çubuğu değiştirmek zorunda değil mi?
anijhaw

1
İstisna'yı yakalarsanız (yukarıda düzenlenmiştir), herhangi bir standart kütüphane istisnasını (İstisnadan devralan ve İstisna .__ init__ olarak adlandırılanlar) yakalayabilirsiniz.
Steve Howard

6
daha eksiksiz / kooperatif olmak için, orijinal grubun diğer bölümlerini e.args = ('mynewstr' + e.args[0],) + e.args[1:]
ekleyin

1
@ nmz787 Bu aslında Python 3 için en iyi çözümdür. Hatanız tam olarak nedir?
Christian

1
@ Dubslow ve martineau Önerilerinizi bir düzenlemeye dahil ettim.
Christian

9

Verilen cevapların hepsini beğenmedim. Onlar hala çok ayrıntılı imho. Kod ve ileti çıktısında.

Sahip olmak istediğim şey, kaynak istisnasına işaret eden yığın izidir, aralarında istisna yoktur, bu nedenle yeni istisnalar yaramaz, sadece orijinali içindeki tüm ilgili yığın çerçeve durumlarıyla yeniden yükseltir, oraya götürür.

Steve Howard , sadece python 3'e uzatmak istediğim güzel bir cevap verdi.

except Exception as e:
    e.args = ("Some failure state", *e.args)
    raise

Tek yeni şey, kullanmam için yeterince küçük ve kolay hale getiren genişletme / açma parametresidir .

Dene:

foo = None

try:
    try:
        state = "bar"
        foo.append(state)

    except Exception as e:
        e.args = ("Appending '"+state+"' failed", *e.args)
        raise

    print(foo[0]) # would raise too

except Exception as e:
    e.args = ("print(foo) failed: " + str(foo), *e.args)
    raise

Bu size:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    foo.append(state)
AttributeError: ('print(foo) failed: None', "Appending 'bar' failed", "'NoneType' object has no attribute 'append'")

Basit ve güzel bir baskı,

print("\n".join( "-"*i+" "+j for i,j in enumerate(e.args)))

5

Sınıf özelliğine hem sınıf nesnesinden hem de sınıf örneğinden erişilebildiğinden, kullandığım kullanışlı bir yaklaşım, sınıf özniteliğini ayrıntılar için depolama alanı olarak kullanmaktır:

class CustomError(Exception):
    def __init__(self, details: Dict):
        self.details = details

Sonra kodunuzda:

raise CustomError({'data': 5})

Ve bir hata yakalarken:

except CustomError as e:
    # Do whatever you want with the exception instance
    print(e.details)

OP, orijinal istisna atıldığında ve yakalanmadığında ayrıntıların yığın izlemesinin bir parçası olarak yazdırılmasını istediğinden gerçekten yararlı değildir.
cowbert

Bence çözüm iyi. Ancak açıklama doğru değil. Sınıf öznitelikleri, örneklendirdiğinizde örneklere kopyalanır. Dolayısıyla, örneğin "ayrıntılar" özniteliğini değiştirdiğinizde, sınıf özniteliği yine de Yok olur. Her neyse, bu davranışı burada istiyoruz.
Adam Wallner

2

Önceki cevapların aksine, bu gerçekten kötü istisnalar karşısında çalışır __str__. Bu yapar yardımcı faktörü için, ancak türünü değiştirmek __str__uygulamaları.

Yine de türü değiştirmeyen ek bir iyileştirme bulmak istiyorum.

from contextlib import contextmanager
@contextmanager
def helpful_info():
    try:
        yield
    except Exception as e:
        class CloneException(Exception): pass
        CloneException.__name__ = type(e).__name__
        CloneException.__module___ = type(e).__module__
        helpful_message = '%s\n\nhelpful info!' % e
        import sys
        raise CloneException, helpful_message, sys.exc_traceback


class BadException(Exception):
    def __str__(self):
        return 'wat.'

with helpful_info():
    raise BadException('fooooo')

Orijinal geri izleme ve tür (ad) korunur.

Traceback (most recent call last):
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
  File "/usr/lib64/python2.6/contextlib.py", line 34, in __exit__
    self.gen.throw(type, value, traceback)
  File "re_raise.py", line 5, in helpful_info
    yield
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
__main__.BadException: wat.

helpful info!

2

Bir özel duruma fazladan bilgi eklemek istediğimde sık kullandığım bir kod snippet'i sunacağım. Hem Python 2.7 hem de 3.6'da çalışıyorum.

import sys
import traceback

try:
    a = 1
    b = 1j

    # The line below raises an exception because
    # we cannot compare int to complex.
    m = max(a, b)  

except Exception as ex:
    # I create my  informational message for debugging:
    msg = "a=%r, b=%r" % (a, b)

    # Gather the information from the original exception:
    exc_type, exc_value, exc_traceback = sys.exc_info()

    # Format the original exception for a nice printout:
    traceback_string = ''.join(traceback.format_exception(
        exc_type, exc_value, exc_traceback))

    # Re-raise a new exception of the same class as the original one, 
    # using my custom message and the original traceback:
    raise type(ex)("%s\n\nORIGINAL TRACEBACK:\n\n%s\n" % (msg, traceback_string))

Yukarıdaki kod aşağıdaki çıktıyı verir:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-09b74752c60d> in <module>()
     14     raise type(ex)(
     15         "%s\n\nORIGINAL TRACEBACK:\n\n%s\n" %
---> 16         (msg, traceback_string))

TypeError: a=1, b=1j

ORIGINAL TRACEBACK:

Traceback (most recent call last):
  File "<ipython-input-6-09b74752c60d>", line 7, in <module>
    m = max(a, b)  # Cannot compare int to complex
TypeError: no ordering relation is defined for complex numbers


Bunun soruda verilen örnekten biraz saptığını biliyorum, ama yine de umarım birisi bunu faydalı bulur.


1

Başka birinden miras alan kendi istisnanızı tanımlayabilir ve değeri ayarlamak için kendi yapıcısını oluşturabilirsiniz.

Örneğin:

class MyError(Exception):
   def __init__(self, value):
     self.value = value
     Exception.__init__(self)

   def __str__(self):
     return repr(self.value)

2
messageOrijinal istisna için bir şey değiştirme / ekleme ihtiyacını ele almıyor (ama düzeltilebilir, sanırım).
martineau

-6

Olabilir

except Exception as e:
    raise IOError(e.message + 'happens at %s'%arg1)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.