अपवाद में जानकारी जोड़ना?


142

मैं कुछ इस तरह से हासिल करना चाहता हूं:

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')

लेकिन मुझे जो मिलता है वह है:

Traceback..
  IOError('Stuff')

कैसे प्राप्त करने के लिए के रूप में कोई सुराग? इसे पायथन 2 और 3 दोनों में कैसे करें?


अपवाद messageविशेषता के लिए दस्तावेज़ीकरण की तलाश करते हुए मुझे यह SO प्रश्न मिला, BaseException.message को Python 2.6 में पदावनत किया गया , जो यह दर्शाता है कि इसका उपयोग अब हतोत्साहित किया गया है (और यह डॉक्स में क्यों नहीं है)।
मार्टीन्यू

दुख की बात है कि वह लिंक अब काम नहीं कर रहा है।
माइकल स्कॉट कटहबर्ट

1
@MichaelScottCuthbert यहां एक अच्छा विकल्प है: itmaybeahack.com/book/python-2.6/html/p02/…
Niels Keurentjes

यहां संदेश विशेषता की स्थिति और आर्ग्स विशेषता और PEP 352 से इसके संबंध की वास्तव में अच्छी व्याख्या है । यह स्टीवन एफ। लोट द्वारा पाइथन में मुफ्त पुस्तक भवन कौशल से है
मार्टिउ

जवाबों:


118

मैं इसे इस तरह करना चाहूंगा ताकि इसके प्रकार को foo()बदलने की आवश्यकता न पड़े, इसे भी इसमें बदलना होगा 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

अपडेट १

यहाँ एक मामूली संशोधन है जो मूल ट्रेसबैक को संरक्षित करता है:

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

अपडेट २

पायथन 3.x के लिए, मेरे पहले अपडेट में कोड वाक्य-रचना के गलत होने के साथ साथ एक होने का विचार है message 2012-05-16 को पीईपी 352 में एक बदलाव के लिए विशेषताBaseException को वापस ले लिया गया था (मेरा पहला अपडेट 2012-03-12 को पोस्ट किया गया था) । तो वर्तमान में, Python 3.5.2 में, वैसे भी, आपको ट्रेसबैक को संरक्षित करने और फ़ंक्शन में अपवाद के प्रकार को हार्डकोड नहीं करने के लिए इन पंक्तियों के साथ कुछ करने की आवश्यकता होगी bar()। यह भी ध्यान दें कि लाइन होगी:

During handling of the above exception, another exception occurred:

प्रदर्शित ट्रेसबैक संदेशों में।

# 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')

अपडेट ३

एक टिप्पणीकार ने पूछा कि क्या कोई ऐसा तरीका है जो पायथन 2 और 3 में काम करेगा। हालाँकि, वाक्यविन्यास मतभेदों के कारण उत्तर "नहीं" प्रतीत हो सकता है, है की तरह एक सहायक फ़ंक्शन का उपयोग करके कि चारों ओर एक रास्ता reraise()मेंsix add- मॉड्यूल पर। इसलिए, यदि आप किसी कारण से लाइब्रेरी का उपयोग नहीं करेंगे, तो नीचे एक सरल स्टैंडअलोन संस्करण है।

यह भी ध्यान दें, कि चूंकि reraise()फ़ंक्शन के भीतर अपवाद को अपवादित किया गया है, इसलिए जो भी ट्रेसबैक उठाया जाता है, उसमें दिखाई देगा, लेकिन अंतिम परिणाम वही है जो हम चाहते हैं।

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
कि मौजूदा अपवाद को जानकारी जोड़ने के बिंदु को हराने के लिए, बैकट्रेस खो देता है। इसके अलावा, यह ctor के अपवाद के साथ काम नहीं करता है> 1 तर्क लेता है (प्रकार कुछ ऐसा है जिसे आप उस जगह से नियंत्रित नहीं कर सकते हैं जहां आप अपवाद को पकड़ते हैं)।
व्लाकव स्लेविक

1
@ वैक्लाव: बैकट्रेस को खोने से रोकने के लिए यह काफी आसान है - जैसा कि मैंने अपडेट में दिखाया है। हालांकि यह अभी भी प्रत्येक बोधगम्य अपवाद को नहीं संभालता है, यह ओपी के प्रश्न में दिखाए गए मामलों के समान काम करता है।
मार्टीन्यू

1
यह काफी सही नहीं है । यदि टाइप (e) ओवरराइड करता है __str__, तो आपको अवांछनीय परिणाम मिल सकते हैं। यह भी ध्यान दें कि दूसरा तर्क पहले तर्क द्वारा दिए गए कंस्ट्रक्टर को दिया जाता है, जो कुछ हद तक निरर्थक है type(e)(type(e)(e.message)। तीसरा, e.essage को e.args के पक्ष में हटा दिया गया है [को ०]।
१०

1
तो, वहाँ एक पोर्टेबल तरीका नहीं है जो पायथन 2 और 3 दोनों में काम करता है?
एलियास डोर्नेलेस

1
@martineau ब्लॉक को छोड़कर आयात करने का उद्देश्य क्या है? क्या यह केवल आवश्यक होने पर आयात करके मेमोरी को बचाने के लिए है?
AllTradesJack

115

यदि आप यहां आए तो पाइथन 3 के लिए एक समाधान खोजने के लिए मैनुअल कहता है:

एक नया अपवाद उठाते समय ( raiseवर्तमान में संभाला जा रहा अपवाद को फिर से बढ़ाने के लिए नंगे का उपयोग करने के बजाय ), अंतर्निहित अपवाद संदर्भ को बढ़ाने के साथ उपयोग करके एक स्पष्ट कारण के साथ पूरक किया जा सकता है:

raise new_exc from original_exc

उदाहरण:

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

जो अंत में इस तरह दिखता है:

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.

TypeErrorमूल अपवाद को गड़बड़ाने के बिना एक समाधान की ओर संकेत के साथ एक अच्छी संदेश में एक पूरी तरह से नॉन्डिसस्क्रिप्ट को चालू करना ।


14
यह सबसे अच्छा समाधान है, क्योंकि परिणामस्वरूप अपवाद मूल कारण पर वापस जाता है, और अधिक विस्तार से आपूर्ति करता है।
JT

क्या कोई उपाय है जिससे हम कुछ संदेश जोड़ सकते हैं लेकिन फिर भी एक नया अपवाद नहीं उठाएँगे? मेरा मतलब है कि सिर्फ अपवाद उदाहरण के संदेश का विस्तार करें।
edcam

हां ~~ यह काम करता है, लेकिन यह कुछ ऐसा महसूस करता है जो मुझे नहीं करना चाहिए। संदेश में संग्रहीत किया जाता है e.args, लेकिन यह एक Tuple है, इसलिए इसे बदला नहीं जा सकता। तो पहले argsएक सूची में कॉपी करें , फिर इसे संशोधित करें, फिर इसे एक ट्यूपल के रूप में कॉपी करें:args = list(e.args) args[0] = 'bar' e.args = tuple(args)
क्रिस

27

मान लें कि आप फू () को संशोधित नहीं कर सकते या नहीं कर सकते, तो आप यह कर सकते हैं:

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

यह वास्तव में यहां एकमात्र समाधान है जो पायथन 3 में समस्या को एक बदसूरत और भ्रामक के बिना हल करता है "उपरोक्त अपवाद से निपटने के दौरान, एक और अपवाद हुआ" संदेश।

यदि री-राइजिंग लाइन को स्टैक ट्रेस में जोड़ा जाना चाहिए, तो raise eइसके बजाय लेखन raiseकरना होगा।


लेकिन इस मामले में अगर अपवाद फू में बदलता है, तो मुझे बार को भी सही बदलना होगा?
आइजॉव

1
यदि आप अपवाद को पकड़ते हैं (ऊपर संपादित किया गया है), तो आप किसी भी मानक पुस्तकालय अपवाद को पकड़ सकते हैं (साथ ही जो अपवाद से प्राप्त होते हैं और अपवाद .__ init__ कहते हैं)।
स्टीव हॉवर्ड

6
अधिक पूर्ण / सहकारी होने के लिए, मूल e.args = ('mynewstr' + e.args[0],) + e.args[1:]
टुपल

1
@ nmz787 यह वास्तव में पायथन 3 के लिए सबसे अच्छा समाधान है। वास्तव में आपकी क्या त्रुटि है?
ईसाई

1
@Dubslow और Martineau मैंने आपके सुझावों को एक संपादन में शामिल किया।
क्रिश्चियन

9

मुझे अब तक दिए गए सभी जवाब पसंद नहीं हैं। वे अभी भी बहुत imbose imho हैं। कोड और संदेश आउटपुट दोनों में।

मैं चाहता हूं कि स्रोत अपवाद को इंगित करता स्टैकट्रेस है, बीच में कोई अपवाद सामान नहीं है, इसलिए नए अपवादों का कोई सृजन नहीं है, बस सभी प्रासंगिक स्टैक फ्रेम राज्यों के साथ मूल को फिर से ऊपर उठाना है, जो वहां का नेतृत्व किया।

स्टीव हॉवर्ड ने एक अच्छा जवाब दिया जो मैं विस्तार करना चाहता हूं, नहीं, कम करें ... केवल अजगर 3 तक।

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

केवल नई चीज पैरामीटर विस्तार / अनपैकिंग है जो मुझे उपयोग करने के लिए छोटा और आसान बनाती है।

कोशिश करो:

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

यह आपको देगा:

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'")

एक साधारण सुंदर प्रिंट की तरह कुछ हो सकता है

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

5

एक आसान तरीका जो मैंने उपयोग किया है वह विवरण के लिए भंडारण के रूप में वर्ग विशेषता का उपयोग करना है, क्योंकि वर्ग विशेषता वर्ग वस्तु और वर्ग उदाहरण दोनों से सुलभ है:

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

फिर अपने कोड में:

raise CustomError({'data': 5})

और जब एक त्रुटि पकड़:

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

वास्तव में उपयोगी नहीं है क्योंकि ओपी अनुरोध कर रहा है कि विवरण को स्टैक ट्रेस के हिस्से के रूप में मुद्रित किया जाए जब मूल अपवाद फेंक दिया जाता है और पकड़ा नहीं जाता है।
काऊबर्ट जूल

मुझे लगता है कि समाधान अच्छा है। लेकिन वर्णन सत्य नहीं है। जब आप उन्हें त्वरित करते हैं, तब क्लास की विशेषताओं को उदाहरणों में कॉपी किया जाता है। इसलिए जब आप विशेषता "विवरण" को संशोधित करते हैं, उदाहरण के लिए, वर्ग विशेषता अभी भी कोई नहीं होगी। वैसे भी हम यहां यही व्यवहार चाहते हैं।
एडम वाल्नर

2

पिछले उत्तरों के विपरीत, यह वास्तव में खराब के अपवादों के साथ काम करता है __str__। यह हालांकि, अनपेक्षित कार्यान्वयनों को समाप्त करने के लिए प्रकार को संशोधित करता है__str__

मैं अभी भी एक अतिरिक्त सुधार ढूंढना चाहता हूं जो कि प्रकार को संशोधित नहीं करता है।

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')

मूल ट्रेसबैक और प्रकार (नाम) संरक्षित हैं।

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

जब भी मैं एक अपवाद के लिए अतिरिक्त जानकारी जोड़ना चाहता / चाहती हूं, तो मैं कोड का एक स्निपेट प्रदान करूंगी जिसका उपयोग मैं अक्सर करती हूं। मैं पायथन 2.7 और 3.6 दोनों में काम करता हूं।

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

निम्नलिखित आउटपुट में कोड ऊपर है:

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


मुझे पता है कि यह प्रश्न में दिए गए उदाहरण से थोड़ा विचलित करता है, लेकिन फिर भी मुझे उम्मीद है कि कोई इसे उपयोगी पाता है।


1

आप अपने स्वयं के अपवाद को परिभाषित कर सकते हैं जो दूसरे से विरासत में मिला है और मूल्य निर्धारित करने के लिए खुद का निर्माता है।

उदाहरण के लिए:

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

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

2
messageमूल अपवाद के लिए कुछ को बदलने / जोड़ने की आवश्यकता नहीं है (लेकिन तय किया जा सकता है, मुझे लगता है)।
मार्टिउ

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.