Aynı İstisna'yı Python'da özel bir mesajla nasıl yükseltebilirim?


145

tryBenim kodda bu blok var :

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

Açıkçası, aslında atılan değil, bu durumda atıfta bulunulan bir başkasını yükseltiyorum . Özel mesajı nasıl eklerim ? Aşağıdaki kodu denemek ama bir örnek , çağrılabilir değil nedeniyle başarısız :ValueErrorValueErrordo_something...()errerrerrValueError

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)

13
@ Hamish, ek bilgi eklemek ve istisnaları yeniden oluşturmak hata ayıklama sırasında çok yardımcı olabilir.
Johan Lundberg

@Johan Kesinlikle - ve bir yığın izleme bunun için. Yeni bir hata oluşturmak yerine neden mevcut hata mesajını düzenlediğinizi tam olarak anlayamıyorum.
Hamish

@Hamish. Tabii ama başka şeyler de ekleyebilirsiniz. Sorunuz için cevabım ve UnicodeDecodeError örneğine bir göz atın. Bu konuda yorumlarınız varsa, bunun yerine cevabımı yorumlayın.
Johan Lundberg


1
@Kit 2020 ve python 3 her yerde. Kabul edilen cevabı neden Ben'in cevabına değiştirmiyorsun :-)
mit

Yanıtlar:


88

Güncelleme: Python 3 için Ben'in cevabını kontrol edin


Geçerli özel duruma bir ileti eklemek ve yeniden yükseltmek için: (dış deneme / hariç, yalnızca etkiyi göstermek içindir)

Python 2.x için burada x> = 6:

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

Bu aynı zamanda doğru olanı yapacağız eğer erredilmektedir türetilmiş gelen ValueError. Örneğin UnicodeDecodeError.

İstediğinizi ekleyebileceğinizi unutmayın err. Örneğin err.problematic_array=[1,2,3].


Düzenleme: @Ducan bir yorumda yukarıdaki python 3 ile çalışmıyor çünkü üye .messagedeğildir ValueError. Bunun yerine bunu kullanabilirsiniz (geçerli python 2.6 veya üstü veya 3.x):

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

Edit2:

Amacın ne olduğuna bağlı olarak, kendi değişken adınıza fazladan bilgi eklemeyi de tercih edebilirsiniz. Hem python2 hem de python3 için:

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

9
Python 3 stil istisna işlemeyi kullanma çabasına gittiğiniz için ve istisnalarda printherhangi bir messageözellik olmadığından muhtemelen kodunuzun Python 3.x'de çalışmadığını unutmayın . err.args = (err.args[0] + " hello",) + err.args[1:]daha güvenilir bir şekilde çalışabilir (ve daha sonra iletiyi almak için bir dizeye dönüştürülebilir).
Duncan

1
Ne yazık ki, args [0] 'un hata mesajını temsil eden bir dize türü olduğuna dair bir garanti yoktur - "İstisna yapıcısına verilen argümanlar kümesi. Bazı yerleşik istisnalar (IOError gibi) belirli sayıda argüman bekler ve diğerlerine genellikle bir hata mesajı veren tek bir dize denir. " Dolayısıyla kod çalışmaz arg [0] bir hata mesajı değildir (int olabilir veya bir dosya adını temsil eden bir dize olabilir).
Trent

1
@Taras, İlginç. Bununla ilgili bir referansınız var mı? Sonra tamamen yeni bir üyeye eklerdim: err.my_own_extra_info. Ya da yeni ve orijinal bilgileri saklayarak hepsini kendi istisnalarımda kapsülleyin.
Johan Lundberg

2
Args [0] 'ın bir hata mesajı olmadığına dair gerçek bir örnek - docs.python.org/2/library/exceptions.html - "istisna EnvironmentError Python sistemi dışında oluşabilecek istisnalar için temel sınıf: IOError, OSError. Bu türden özel durumlar 2 parçalı olarak oluşturulduğunda, ilk öğe örneğin errno özniteliğinde (bir hata numarası olduğu varsayılır) kullanılabilir ve ikinci öğe strerror özniteliğinde bulunur (genellikle ilişkilendirilir tuple, args özniteliğinde de bulunur. "
Trent

2
Bunu hiç anlamıyorum. .messageBurada özelliğin ayarlanmasının tek nedeni , bu özelliğin açıkça yazdırılmış olmasıdır. Eğer alıcı ve yazdırmadan istisna yükseltmek olsaydı, olurdu değil bakın .messagenitelik yararlı bir şey yok.
DanielSank

171

Sadece python 3.x'i destekleyecek kadar şanslıysanız, bu gerçekten bir güzellik haline gelir :)

yükseltmek

Biz kullanarak istisnaları zincir olabilir gelen zam .

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

Bu durumda, arayanınızın yakalayacağı istisna, istisnanızı yükselttiğimiz yerin satır numarasına sahiptir.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

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

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

Alt kural dışı durumun yalnızca kural dışı durumumuzu yükselttiğimiz yerden gelen yığın izine sahip olduğuna dikkat edin. Arayan, __cause__yakaladığı istisnanın özelliğine erişerek orijinal istisnayı almaya devam edebilir .

with_traceback

Veya with_traceback ile kullanabilirsiniz .

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

Bu formu kullanarak, arayanın yakalayacağı istisna, orijinal hatanın oluştuğu yerden izlemeye sahiptir.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

Alt kural dışı durumun, geçersiz bölümü gerçekleştirdiğimiz satırın yanı sıra kural dışı durumu yeniden oluşturduğumuz satır olduğuna dikkat edin.


1
Ek geri izleme olmadan özel duruma özel bir mesaj eklemek mümkün müdür? Örneğin, raise Exception('Smelly socks') from eyalnızca yeni bir geri izleme getirmek yerine orijinal geri izlemeye bir yorum olarak "Koklamak çorap" eklemek için değiştirilebilir.
joelostblom

Johan Lundberg'in cevabından alacağınız davranış bu
Ben

3
bu gerçekten çok güzel. Teşekkür ederim.
allanberry

3
Yeni bir özel durumun yeniden oluşturulması veya yeni mesajlarla zincir yükseltme istisnaları birçok durumda gerekenden daha fazla karışıklık yaratır. Tek başına istisnaların üstesinden gelmek karmaşıktır. Daha iyi bir strateji, err.args + = ("message",) 'da olduğu gibi mesajınızı mümkünse orijinal istisna argümanına eklemek ve istisna mesajını yeniden yükseltmektir. Geri izleme sizi istisnanın yakalandığı satır numaralarına götürmeyebilir, ancak istisnanın kesin olarak gerçekleştiği yere götürür.
user-asterix

2
Ayrıca , yan tümcesinde Yok'u belirterek istisna zincirinin görüntüsünü açıkça bastırabilirsiniz :raise RuntimeError("Something bad happened") from None
pfabri

10
try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

baskılar:

There is a problem: invalid literal for int() with base 10: 'a'

1
Başka bir örneği yükseltmekten başka , yapmaya çalıştığım şey için bir Python deyimi olup olmadığını merak ediyordum .
Kit

@Kit - Buna 'bir istisnayı yeniden gündeme getirmek' derdim
eumiro

1
@eumiro, Hayır, yeni bir istisna yapıyorsun. Cevabımı gör. Bağlantınızdan: "... ancak yeniden oluşturulacak istisna mevcut kapsamdaki en son etkin istisna ise, ifadesiz yükseltme tercih edilmelidir."
Johan Lundberg

3
@JohanLundberg - raiseparametreler olmadan yeniden yükseliyor . OP bir mesaj eklemek istiyorsa, yeni bir istisna oluşturmalıdır ve orijinal istisnanın mesajını / türünü tekrar kullanabilir.
eumiro

2
İsterseniz eklemek mesaj size "ValueError" atarak sıfırdan yeni bir mesaj oluşturmak mümkün değil. Böylece, ne tür bir ValueError olduğuna dair temel bilgileri yok edersiniz (C ++ dilimlemeye benzer). Tarafından yeniden atma , aynı bir bağımsız değişken olmayan arttırarak durum, sen (ValueError elde edilir) bu doğru belirli bir tip özgün nesnesi iletir.
Johan Lundberg

9

Görünüşe göre tüm cevaplar e.args [0] 'a bilgi ekleyerek mevcut hata mesajını değiştiriyor. Bunun yerine, args grubunu genişletmenin bir dezavantajı var mı? Olası ters olduğunu düşünüyorum, bu dize ayrıştırma gerekli durumlarda tek başına orijinal hata mesajı bırakabilirsiniz; ve özel hata işleme işleminiz, geri izlemenin programlı olarak (sistem izleme aracı gibi) ayrıştırılacağı durumlar için birkaç mesaj veya hata kodu üretmişse, gruba birden çok öğe ekleyebilirsiniz.

## Approach #1, if the exception may not be derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

veya

## Approach #2, if the exception is always derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

Bu yaklaşımın bir dezavantajı görebiliyor musunuz?


Eski cevabım e.args [0] 'ı değiştirmiyor.
Johan Lundberg

4

Bu kod şablonu, özel bir iletiyle bir istisna oluşturmanıza izin vermelidir.

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

3
Bu yığın izini korumaz.
plok

Soru soran, yığın izlemesinin korunacağını belirtmez.
shrewmouse

4

Kullanarak hata mesajınızla yeni istisnayı yükseltin

raise Exception('your error message')

veya

raise ValueError('your error message')

yükseltmek istediğiniz yerde VEYA 'from' kullanarak geçerli istisnaya hata mesajı ekleyin (değiştirin) (yalnızca Python 3.x desteklenir):

except ValueError as e:
  raise ValueError('your message') from e

Thanx, @gberger, 'e' yaklaşımı aslında python 2.x tarafından desteklenmiyor
Alexey Antonenko

3

Bu, orijinal geri izleme korunurken Python 2.7 ve 3.x özel durum iletisini değiştirmek için kullandığım işlevdir. Gerektirirsix

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.

    Preserves exception class, and exception traceback.

    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.

    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending

    Returns:
        None

    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

3

Python 3 yerleşik istisnaları aşağıdaki strerroralana sahiptir:

except ValueError as err:
  err.strerror = "New error message"
  raise err

Bu işe yaramıyor gibi görünüyor. Bir şey mi eksik?
MasayoMusic

2

Geçerli cevap benim için iyi çalışmadı, istisna yeniden yakalanmazsa ekteki mesaj gösterilmez.

Ancak aşağıdaki gibi yapılması, izlemeyi korur ve istisna yeniden yakalanıp yakalanmamasına bakılmaksızın ekli mesajı gösterir.

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(Python 2.7 kullandım, Python 3'te denemedim)


1

Yukarıdaki çözümlerin hiçbiri tam olarak ne istediğimi yapmadı, bu da hata mesajının ilk kısmına bazı bilgiler eklemekti, yani kullanıcılarımın önce özel mesajımı görmesini istedim.

Bu benim için çalıştı:

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True

if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

0

Bu sadece Python 3 ile çalışır . Kural dışı durumun özgün bağımsız değişkenlerini değiştirebilir ve kendi bağımsız değişkenlerinizi ekleyebilirsiniz.

Bir istisna, oluşturulduğu argümanları hatırlar. Bunun istisnayı değiştirebilmeniz için olduğunu düşünüyorum.

Fonksiyonda reraisebiz (mesaj gibi) istedikleri herhangi bir yeni argümanlarla istisnanın orijinal argümanları getirebilirsiniz. Sonunda geri izleme geçmişini korurken istisnayı yeniden gündeme getiriyoruz.

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''

  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args

  # Recreate the expection and preserve the traceback info so thta we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   


def bad():
  raise ValueError('bad')

def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')

def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')

very_very()

çıktı

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

-3

hata türünü özelleştirmek istiyorsanız, yapabileceğiniz basit bir şey ValueError'a dayalı bir hata sınıfı tanımlamaktır.


bu durumda bu nasıl yardımcı olur?
Johan Lundberg
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.