"आंतरिक अपवाद" (ट्रेसबैक के साथ) पायथन में?


146

मेरी पृष्ठभूमि C # में है और मैंने हाल ही में पायथन में प्रोग्रामिंग शुरू की है। जब एक अपवाद को फेंक दिया जाता है तो मैं आमतौर पर इसे दूसरे अपवाद में लपेटना चाहता हूं जो अधिक जानकारी जोड़ता है, जबकि अभी भी पूर्ण स्टैक ट्रेस दिखा रहा है। यह सी # में काफी आसान है, लेकिन मैं इसे पायथन में कैसे करूं?

उदाहरण के लिए। C # में मैं ऐसा कुछ करूंगा:

try
{
  ProcessFile(filePath);
}
catch (Exception ex)
{
  throw new ApplicationException("Failed to process file " + filePath, ex);
}

अजगर में मैं कुछ ऐसा ही कर सकता हूं:

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file ' + filePath, e)

... लेकिन यह आंतरिक अपवाद का ट्रेसबैक खो देता है!

संपादित करें: मैं दोनों अपवाद संदेशों को देखना चाहता हूं और दोनों निशान मिटाते हैं और दोनों को सहसंबंधित करते हैं। यही है, मैं आउटपुट में देखना चाहता हूं कि अपवाद एक्स यहां हुआ और फिर अपवाद वाई वहां - जैसा कि मैं सी # में होगा। क्या पायथन 2.6 में यह संभव है? ऐसा लगता है कि मैं अब तक सबसे अच्छा कर सकता हूं (ग्लेन मेनार्ड के जवाब के आधार पर):

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]

इसमें संदेश और ट्रेसबैक दोनों शामिल हैं, लेकिन यह नहीं दिखाता है कि ट्रेसबैक में कौन सा अपवाद हुआ।


3
स्वीकृत उत्तर पुराना हो रहा है, शायद आपको दूसरे को स्वीकार करने पर विचार करना चाहिए।
एरन हॉल

1
@AaronHall दुर्भाग्य से ओपी को 2015 के बाद से चारों ओर नहीं देखा गया है
एंटटी हापला

जवाबों:


136

अजगर २

यह आसान है; तीसरे तर्क के रूप में ट्रेसबैक पास करें।

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

एक अपवाद को पकड़ने और दूसरे को फिर से ऊपर उठाने पर हमेशा ऐसा करें।


4
धन्यवाद। यह ट्रेसबैक सुरक्षित रखता है, लेकिन यह मूल अपवाद के त्रुटि संदेश को खो देता है। मैं संदेश और ट्रेसबैक दोनों कैसे देखूं?
ईएमपी

6
raise MyException(str(e)), ..., आदि
ग्लेन मेनार्ड

23
पायथन 3 जोड़ता है raise E() from tbऔर.with_traceback(...)
दिमा तिस्नेक

3
@GlennMaynard यह एक बहुत पुराना सवाल है, लेकिन raiseअपवाद को पारित करने के लिए मूल्य का मध्य तर्क है (यदि पहला तर्क अपवाद वर्ग है और उदाहरण नहीं है)। इसलिए यदि आप अपवादों को बदलना चाहते हैं, तो करने के बजाय raise MyException(str(e)), None, sys.exc_info()[2], इसका उपयोग करना बेहतर है raise MyException, e.args, sys.exc_info()[2]:।
सुबह

8
भविष्य के पैकेज का उपयोग करके पायथन 2 और 3 आज्ञाकारी तरीका संभव है: python-future.org/compatible_idioms.html#raising-exception Eg from future.utils import raise_और raise_(ValueError, None, sys.exc_info()[2])
jtpereyda

239

अजगर ३

अजगर 3 में आप निम्न कार्य कर सकते हैं:

try:
    raise MyExceptionToBeWrapped("I have twisted my ankle")

except MyExceptionToBeWrapped as e:

    raise MyWrapperException("I'm not in a good shape") from e

यह कुछ इस तरह का उत्पादन करेगा:

   Traceback (most recent call last):
   ...
   MyExceptionToBeWrapped: ("I have twisted my ankle")

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

   Traceback (most recent call last):
   ...
   MyWrapperException: ("I'm not in a good shape")

17
raise ... from ...वास्तव में पाइथन 3 में ऐसा करने का सही तरीका है। इसके लिए और अधिक अपवोट चाहिए।
16

Nakedibleमुझे लगता है कि ऐसा इसलिए है क्योंकि दुर्भाग्य से अधिकांश लोग अभी भी पायथन 3 का उपयोग नहीं कर रहे हैं
टिम लुड्विंस्की

ऐसा लगता है कि अजगर 3 में 'से' का उपयोग करने के साथ भी होता है
स्टीव वर्म्यूलेन

अजगर को वापस भेजा जा सकता है। आशा है कि यह एक दिन होगा।
मार्सिन वोज्नार्स्की

4
@ogrisel आप इसे futureप्राप्त करने के लिए पैकेज का उपयोग कर सकते हैं : python-future.org/compatible_idioms.html#raising-exception Eg from future.utils import raise_और raise_(ValueError, None, sys.exc_info()[2])
jtpereyda

19

पायथन 3 में है raise...from श्रृंखला के अपवादों का खंडग्लेन का जवाब पायथन 2.7 के लिए बहुत अच्छा है, लेकिन यह केवल मूल अपवाद के ट्रेसबैक का उपयोग करता है और त्रुटि संदेश और अन्य विवरण को दूर फेंक देता है। यहाँ पायथन 2.7 में कुछ उदाहरण दिए गए हैं जो वर्तमान अपवाद से संदर्भ जानकारी को मूल अपवाद के त्रुटि संदेश में जोड़ते हैं, लेकिन अन्य विवरणों को अक्षुण्ण रखते हैं।

ज्ञात अपवाद प्रकार

try:
    sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
    self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
    _, ex, traceback = sys.exc_info()
    message = "Connecting to '%s': %s." % (config['connection'],
                                           ex.strerror)
    raise IOError, (ex.errno, message), traceback

raiseबयान का यह स्वाद पहली अभिव्यक्ति के रूप में अपवाद प्रकार लेता है, अपवाद वर्ग रचनाकार एक अभिव्यक्ति में दूसरी अभिव्यक्ति के रूप में, और तीसरी अभिव्यक्ति के रूप में ट्रेसबैक। यदि आप पायथन 2.2 से पहले चल रहे हैं, तो चेतावनी देखें sys.exc_info()

कोई अपवाद प्रकार

यहाँ एक और उदाहरण है जो कि अधिक सामान्य उद्देश्य है यदि आपको नहीं पता कि आपके कोड को किस प्रकार के अपवादों को पकड़ना पड़ सकता है। नकारात्मक पक्ष यह है कि यह अपवाद प्रकार को खो देता है और बस एक रनटाइमइम्र उठाता है। आपको tracebackमॉड्यूल आयात करना होगा ।

except Exception:
    extype, ex, tb = sys.exc_info()
    formatted = traceback.format_exception_only(extype, ex)[-1]
    message = "Importing row %d, %s" % (rownum, formatted)
    raise RuntimeError, message, tb

संदेश को संशोधित करें

यहां एक अन्य विकल्प है यदि अपवाद प्रकार आपको संदर्भ को जोड़ने देगा। आप अपवाद के संदेश को संशोधित कर सकते हैं और फिर उसे reraise कर सकते हैं।

import subprocess

try:
    final_args = ['lsx', '/home']
    s = subprocess.check_output(final_args)
except OSError as ex:
    ex.strerror += ' for command {}'.format(final_args)
    raise

यह निम्नलिखित स्टैक ट्रेस उत्पन्न करता है:

Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
    s = subprocess.check_output(final_args)
  File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']

आप देख सकते हैं कि यह उस रेखा को दिखाता है जहां check_output()कहा गया था, लेकिन अपवाद संदेश में अब कमांड लाइन शामिल है।


1
कहां से ex.strerrorआ रहा है? मुझे पायथन डॉक्स में इसके लिए कोई प्रासंगिक हिट नहीं मिल रहा है। यह नहीं होना चाहिए str(ex)?
हेनरिक हेमबर्गर

1
IOErrorEnvironmentError@hheimbuerger से लिया गया है , जो गुण errornoऔर strerrorविशेषताएं प्रदान करता है ।
डॉन किर्कबी

मैं एक मनमाना Error, उदाहरण के लिए ValueError RuntimeErrorको Exceptionकैसे पकड़ कर लपेटूंगा ? यदि मैं इस मामले के लिए आपके उत्तर को पुन: प्रस्तुत करता हूं, तो स्टैकट्रेस खो जाता है।
कार्ल रिक्टर

मुझे यकीन नहीं है कि आप क्या पूछ रहे हैं, @karl। क्या आप एक नए प्रश्न में एक नमूना पोस्ट कर सकते हैं और फिर इसे यहां से लिंक कर सकते हैं?
डॉन किर्कबी

मैंने ओपी के सवाल के अपने डुप्लिकेट को stackoverflow.com/questions/23157766/… पर एडिट किया और सीधे आपके जवाब को ध्यान में रखते हुए क्लीयरेंस के साथ। हमें वहां चर्चा करनी चाहिए :)
कार्ल रिक्टर

12

में अजगर 3.x :

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

या बस

except Exception:
    raise MyException()

जो प्रचारित करेगा, MyExceptionलेकिन दोनों अपवादों को मुद्रित करेगा यदि इसे नियंत्रित नहीं किया जाएगा।

में अजगर 2.x :

raise Exception, 'Failed to process file ' + filePath, e

आप __context__विशेषता को मारकर दोनों अपवादों को मुद्रित करने से रोक सकते हैं । यहाँ मैंने मक्खी पर आपके अपवाद को पकड़ने और बदलने के लिए एक संदर्भ प्रबंधक लिखा है: ( http://docs.python.org/3.1/library/stdtypes.html विस्तार से देखें कि वे कैसे काम करते हैं)

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise

4
TypeError: उठाना: arg 3 एक ट्रेसबैक या कोई नहीं होना चाहिए
ग्लेन मेनार्ड

क्षमा करें, मैंने एक गलती की, किसी तरह मैंने सोचा कि यह अपवादों को भी स्वीकार करता है और अपने ट्रेसबैक विशेषता को स्वचालित रूप से प्राप्त करता है। Docs.python.org/3.1/reference/… के अनुसार , यह e .__ ट्रेसबैक__
ilya n

1
@ लियान: पायथन 2 में e.__traceback__विशेषता नहीं है !
जनवरी को

5

मुझे नहीं लगता कि आप पायथन 2.x में ऐसा कर सकते हैं, लेकिन इस कार्यक्षमता के समान कुछ अजगर 3 का हिस्सा है। PE4 314 से :

आज के पायथन कार्यान्वयन में, अपवाद तीन भागों से बने हैं: प्रकार, मूल्य और ट्रेसबैक। 'Sys' मॉड्यूल, तीन समानांतर चर, exc_type, exc_value, और exc_traceback, sys.exc_info () फ़ंक्शन में इन तीन भागों के एक टपल को वर्तमान अपवाद को उजागर करता है, और 'बढ़ाएँ' कथन को तीन-तर्क रूप स्वीकार करता है। ये तीन भाग। अपवादों को जोड़ते हुए अक्सर इन तीन चीजों को समानांतर में पारित करने की आवश्यकता होती है, जो थकाऊ और त्रुटि-प्रवण हो सकती है। इसके अतिरिक्त, 'को छोड़कर' बयान केवल मूल्य तक पहुंच प्रदान कर सकता है, ट्रेसबैक नहीं। अपवाद मानों में ' ट्रेसबैक ' विशेषता जोड़ने से सभी अपवाद जानकारी एक ही स्थान से सुलभ हो जाती हैं।

C # की तुलना:

C # में अपवादों में केवल-पढ़ने के लिए 'इनर एक्सेप्शन' गुण होता है जो किसी अन्य अपवाद को इंगित कर सकता है। इसके प्रलेखन [10] का कहना है कि "जब एक अपवाद एक्स को पिछले अपवाद Y के प्रत्यक्ष परिणाम के रूप में फेंक दिया जाता है, तो X की आंतरिक संपत्ति में Y का संदर्भ होना चाहिए।" यह गुण VM द्वारा स्वचालित रूप से सेट नहीं किया गया है; बल्कि, सभी अपवाद निर्माणकर्ता इसे स्पष्ट रूप से सेट करने के लिए एक वैकल्पिक 'इनरसेप्शन' तर्क लेते हैं। ' कारण ' विशेषता इनर एक्सेप्शन के समान उद्देश्य को पूरा करती है, लेकिन यह पीईपी सभी अपवादों के निर्माणकर्ताओं को विस्तारित करने के बजाय 'बढ़ा' के एक नए रूप का प्रस्ताव करता है। C # एक GetBaseException विधि भी प्रदान करता है जो इनरएक्ससेप्शन श्रृंखला के अंत में सीधे कूदता है;

यह भी ध्यान दें कि जावा, रूबी और पर्ल 5 इस प्रकार का समर्थन नहीं करते हैं। फिर से उद्धृत करना:

अन्य भाषाओं के लिए, जावा और रूबी दोनों मूल अपवाद को छोड़ देते हैं जब एक अन्य अपवाद 'पकड़' / 'बचाव' या 'अंत में' / 'सुनिश्चित' खंड में होता है। पर्ल 5 में अंतर्निहित संरचित अपवाद हैंडलिंग की कमी है। पर्ल 6 के लिए, RFC नंबर 88 [9] एक अपवाद तंत्र का प्रस्ताव करता है जो कि #@ नामक एक सरणी में जंजीर अपवादों को बरकरार रखता है।


लेकिन, निश्चित रूप से, पर्ल 5 में आप सिर्फ "कबूल {ओह NOES! $ @}" कह सकते हैं और अन्य अपवाद के स्टैक ट्रेस को नहीं खो सकते हैं। या आप अपने स्वयं के प्रकार को लागू कर सकते हैं जो अपवाद को बरकरार रखता है।
जॉकवे

4

पायथन 2 और 3 के बीच अधिकतम अनुकूलता के लिए, आप लाइब्रेरी raise_fromमें उपयोग कर सकते हैं sixhttps://six.readthedocs.io/#six.raise_from । यहाँ आपका उदाहरण है (स्पष्टता के लिए थोड़ा संशोधित):

import six

try:
  ProcessFile(filePath)
except Exception as e:
  six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)

3

आप Python 2.x (और यहां तक ​​कि Python 3 में भी श्रृंखला के अपवादों के लिए मेरे CausedException वर्ग का उपयोग कर सकते हैं, यह उस स्थिति में उपयोगी हो सकता है जब आप एक नए अपवाद को जन्म देने वाले अपवाद के रूप में एक से अधिक पकड़े गए अपवाद देना चाहते हैं)। शायद यह आपकी मदद कर सकता है।


2

हो सकता है कि आप संबंधित जानकारी को पकड़कर उसे पास कर सकें? मैं कुछ सोच रहा हूँ:

import traceback
import sys
import StringIO

class ApplicationError:
    def __init__(self, value, e):
        s = StringIO.StringIO()
        traceback.print_exc(file=s)
        self.value = (value, s.getvalue())

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

try:
    try:
        a = 1/0
    except Exception, e:
        raise ApplicationError("Failed to process file", e)
except Exception, e:
    print e

2

मान लिया जाये कि:

  • आपको एक समाधान की आवश्यकता है, जो कि पायथन 2 के लिए काम करता है (शुद्ध पायथन 3 के लिए raise ... fromसमाधान देखें )
  • बस त्रुटि संदेश को समृद्ध करना चाहते हैं, जैसे कुछ अतिरिक्त संदर्भ प्रदान करना
  • पूर्ण स्टैक ट्रेस की आवश्यकता है

आप डॉक्स https://docs.python.org/3/tutorial/errors.html#raising-exception से एक सरल समाधान का उपयोग कर सकते हैं :

try:
    raise NameError('HiThere')
except NameError:
    print 'An exception flew by!' # print or log, provide details about context
    raise # reraise the original exception, keeping full stack trace

उत्पादन:

An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

ऐसा लगता है कि कुंजी टुकड़ा सरलीकृत 'बढ़ा' कीवर्ड है जो अकेले खड़ा है। अपवाद को छोड़कर अपवाद को फिर से बढ़ाएगा।


यह पायथन 2 और 3 संगत समाधान है! धन्यवाद!
एंडी चेज़

मुझे लगता है कि विचार एक अलग प्रकार के अपवाद को बढ़ाने के लिए था।
टिम लुडविंस्की

2
यह नेस्टेड अपवादों की श्रृंखला नहीं है, बस एक अपवाद को रेयर करना है
कार्ल रिक्टर

यह सबसे अच्छा अजगर 2 समाधान है, अगर आपको केवल अपवाद संदेश को समृद्ध करने और पूर्ण स्टैक ट्रेस करने की आवश्यकता है!
geekQ

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