मौजूदा जानकारी को संरक्षित करते हुए, एक अलग प्रकार और संदेश के साथ अपवाद फिर से बढ़ाएं


139

मैं एक मॉड्यूल लिख रहा हूं और अपवादों के लिए एक एकीकृत अपवाद पदानुक्रम रखना चाहता हूं जो इसे बढ़ा सकता है (जैसे FooErrorसभी fooमॉड्यूल के विशिष्ट अपवादों के लिए एक सार वर्ग से विरासत में मिला )। इससे मॉड्यूल के उपयोगकर्ता उन विशेष अपवादों को पकड़ सकते हैं और यदि आवश्यक हो, तो उन्हें विशिष्ट रूप से संभाल सकते हैं। लेकिन मॉड्यूल से उठाए गए कई अपवाद कुछ अन्य अपवाद के कारण उठाए गए हैं; उदाहरण के लिए एक फ़ाइल पर OSError के कारण कुछ कार्य में विफल।

मुझे इसकी आवश्यकता है "अपवाद" को लपेटने के लिए, जैसे कि इसका एक अलग प्रकार और संदेश है , ताकि जो भी अपवाद को पकड़ता है, उसके द्वारा प्रचार प्रसार पदानुक्रम तक जानकारी उपलब्ध हो। लेकिन मैं मौजूदा प्रकार, संदेश और स्टैक ट्रेस खोना नहीं चाहता; समस्या को डीबग करने का प्रयास करने वाले व्यक्ति के लिए यह सभी उपयोगी जानकारी है। एक शीर्ष-स्तरीय अपवाद हैंडलर कोई अच्छा नहीं है, क्योंकि मैं अपवाद को सजाने से पहले यह कोशिश कर रहा हूं कि इससे प्रचार ढेर हो जाए, और शीर्ष-स्तरीय हैंडलर बहुत देर हो जाए।

यह आंशिक रूप से मेरे मॉड्यूल fooके विशिष्ट अपवाद प्रकारों को मौजूदा प्रकार (जैसे class FooPermissionError(OSError, FooError)) से प्राप्त करके हल किया जाता है , लेकिन इससे मौजूदा अपवाद उदाहरण को नए प्रकार में लपेटना आसान नहीं होता है, और न ही संदेश को संशोधित किया जा सकता है।

पायथन के PEP 3134 "अपवाद चेनिंग और एंबेडेड ट्रैसबैक्स" अपवाद वस्तुओं को "चेनिंग" करने के लिए पायथन 3.0 में स्वीकार किए गए बदलाव की चर्चा करते हैं, यह इंगित करने के लिए कि मौजूदा अपवाद की हैंडलिंग के दौरान एक नया अपवाद उठाया गया था।

मैं जो करने की कोशिश कर रहा हूं वह संबंधित है: मुझे इसके पहले पायथन संस्करणों में भी काम करने की आवश्यकता है, और मुझे इसकी आवश्यकता है कि मैं इसे जंजीरों के लिए नहीं, बल्कि केवल बहुरूपता के लिए उपयोग करूं। ऐसा करने का सही तरीका क्या है?


अपवाद पहले से ही पूरी तरह से बहुरूपी हैं - वे अपवाद के सभी उपवर्ग हैं। तुम क्या करने की कोशिश कर रहे हो? "अलग संदेश" शीर्ष स्तर के अपवाद हैंडलर के साथ काफी तुच्छ है। आप क्लास क्यों बदल रहे हैं?
एस.लॉट

जैसा कि सवाल में समझाया गया है (अब, आपकी टिप्पणी के लिए धन्यवाद): मैं एक अपवाद को सजाने की कोशिश कर रहा हूं, जिसे मैंने पकड़ा है, ताकि यह और अधिक जानकारी के साथ प्रचार कर सके, लेकिन कोई भी हार नहीं। एक शीर्ष-स्तरीय हैंडलर बहुत देर हो चुकी है।
बिंगोस

कृपया मेरी CausedException class पर एक नज़र डालें जो आप पाइथन 2.x में क्या चाहते हैं। पायथन 3 में भी इसका उपयोग उस मामले में किया जा सकता है जब आप अपने अपवाद के कारण एक से अधिक मूल अपवाद देना चाहते हैं। हो सकता है कि यह आपकी आवश्यकताओं के अनुरूप हो।
एलिफ


अजगर -2 के लिए मैं @DevinJeanpierre के समान कुछ करता हूं, लेकिन मैं सिर्फ एक नया स्ट्रिंग संदेश भेज रहा हूं: except Exception as e-> raise type(e), type(e)(e.message + custom_message), sys.exc_info()[2]-> यह समाधान एक अन्य SO प्रश्न से है । यह सुंदर लेकिन कार्यात्मक नहीं है।
ट्रेवर बॉयड स्मिथ

जवाबों:


197

पायथन 3 ने अपवाद की शुरुआत की (जैसा कि पीईपी 3134 में वर्णित है )। यह अनुमति देता है, जब एक अपवाद को "कारण" के रूप में मौजूदा अपवाद का हवाला देते हुए:

try:
    frobnicate()
except KeyError as exc:
    raise ValueError("Bad grape") from exc

पकड़ा गया अपवाद नए अपवाद का ("कारण" है) का हिस्सा बन जाता है, और जो भी कोड नए अपवाद को पकड़ता है उसके लिए उपलब्ध है।

इस सुविधा का उपयोग करके, __cause__विशेषता सेट की जाती है। अंतर्निहित अपवाद हैंडलर यह भी जानता है कि ट्रेसबैक के साथ अपवाद के "कारण" और "संदर्भ" को कैसे रिपोर्ट किया जाए


में अजगर 2 , ऐसा लगता है इस प्रयोग के मामले कोई अच्छा जवाब (के रूप में द्वारा वर्णित है इयान Bicking और नेड Batchelder )। ओह।


4
क्या इयान बेकिंग मेरे समाधान का वर्णन नहीं करता है? मुझे पछतावा है कि मैंने ऐसा ईश्वरीय जवाब दिया, लेकिन यह अजीब है कि यह स्वीकार हो गया।
डेविन जीनपिएरे

1
@bignose आपको मेरी बात न केवल सही होने से मिली, बल्कि "फ्रोबनीट" के प्रयोग के लिए :)
डेविड एम।

5
अपवाद चाइनिंग वास्तव में अब डिफ़ॉल्ट व्यवहार है, वास्तव में यह इस मुद्दे के विपरीत है, पहले अपवाद को दबाने से काम की आवश्यकता होती है, PEP 409 python.org/dev/peps/pep-0409
क्रिस_रैंड्स

1
आप अजगर 2 में इसे कैसे पूरा करेंगे?
सेलोटेप

1
यह ठीक काम करने लगता है (अजगर 2.7)try: return 2 / 0 except ZeroDivisionError as e: raise ValueError(e)
एलेक्स

37

ट्रेसबैक पाने के लिए आप sys.exc_info () का उपयोग कर सकते हैं, और अपने नए अपवाद को ट्रेसबैक (पीईपी उल्लेख के रूप में) के साथ बढ़ा सकते हैं। यदि आप पुराने प्रकार और संदेश को संरक्षित करना चाहते हैं, तो आप अपवाद पर ऐसा कर सकते हैं, लेकिन यह तभी उपयोगी है जब भी आपका अपवाद इसे पकड़ता है।

उदाहरण के लिए

import sys

def failure():
    try: 1/0
    except ZeroDivisionError, e:
        type, value, traceback = sys.exc_info()
        raise ValueError, ("You did something wrong!", type, value), traceback

बेशक, यह वास्तव में उपयोगी नहीं है। अगर ऐसा होता, तो हमें उस PEP की आवश्यकता नहीं होती। मैं इसे करने की सलाह नहीं दूंगा।


डेविन, आप ट्रेसबैक में एक संदर्भ संग्रहीत करते हैं, क्या आपको उस संदर्भ को स्पष्ट रूप से नहीं हटाना चाहिए?
अराफंगियन

2
मैंने कुछ भी संग्रहीत नहीं किया, मैंने ट्रेसबैक को एक स्थानीय चर के रूप में छोड़ दिया जो संभवतः दायरे से बाहर हो जाता है। हां, यह बोधगम्य है कि यह नहीं करता है, लेकिन यदि आप अपवादों को वैश्विक कार्यक्षेत्र के बजाय इस तरह से उठाते हैं, तो आपके पास बड़े मुद्दे हैं। यदि आपकी शिकायत केवल यह है कि इसे वैश्विक दायरे में क्रियान्वित किया जा सकता है, तो उचित समाधान यह है कि अप्रासंगिक बॉयलरप्लेट को जोड़ा जाए जिसे समझाया जाना चाहिए और 99% उपयोगों के लिए प्रासंगिक नहीं है, लेकिन समाधान को फिर से लिखना है ताकि कोई ऐसी बात न हो यह आवश्यक है जबकि ऐसा लगता है जैसे कि कुछ भी अलग नहीं है - जैसा कि मैंने अब किया है।
डेविन जीनपिएरे

4
Arafangion के लिएsys.exc_info() @ ड्वेन के लिए पाइथन डॉक्यूमेंटेशन में चेतावनी का जिक्र हो सकता है । यह कहता है, "एक अपवाद को संभालने वाले फ़ंक्शन में स्थानीय चर पर ट्रेसबैक रिटर्न मान असाइन करना एक परिपत्र संदर्भ का कारण होगा।" हालांकि, एक निम्नलिखित नोट कहता है कि पायथन 2.2 के बाद से, चक्र को साफ किया जा सकता है, लेकिन यह सिर्फ इसे से बचने के लिए अधिक कुशल है।
डॉन किर्कबी

5
: दो प्रबुद्ध pythonistas से अजगर में फिर से उठाने के अपवाद को अलग अलग तरीकों पर अधिक जानकारी इयान Bicking और नेड Batchelder
Rodrigue

11

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

class NewException(CaughtException):
    def __init__(self, caught):
        self.caught = caught

try:
    ...
except CaughtException as e:
    ...
    raise NewException(e)

लेकिन ज्यादातर समय, मुझे लगता है कि अपवाद को पकड़ना, उसे संभालना और raiseमूल अपवाद (और ट्रेसबैक को संरक्षित करना) या raise NewException()। अगर मैं आपका कोड कॉल कर रहा था, और मुझे आपका एक कस्टम अपवाद मिला, तो मुझे उम्मीद है कि आपके कोड को पहले से जो भी अपवाद आपको पकड़ने थे, वह हाथ लग गया है। इस प्रकार मुझे इसे स्वयं एक्सेस करने की आवश्यकता नहीं है।

संपादित करें: मैंने पाया अपने स्वयं के अपवाद को फेंकने और मूल अपवाद रखने के तरीकों का यह विश्लेषण । कोई सुंदर समाधान नहीं।


1
मेरे द्वारा उपयोग किया गया मामला अपवाद को संभालने के लिए नहीं है; यह विशेष रूप से इसे संभालने के बारे में नहीं है, लेकिन कुछ अतिरिक्त जानकारी (एक अतिरिक्त वर्ग और एक नया संदेश) जोड़ रहा है ताकि इसे कॉल स्टैक तक आगे संभाला जा सके।
बिंगोस

2

मैंने यह भी पाया कि कई बार मुझे उठाए गए त्रुटियों के लिए कुछ "रैपिंग" की आवश्यकता होती है।

इसमें फ़ंक्शन स्कोप दोनों शामिल थे और कभी-कभी किसी फ़ंक्शन के अंदर केवल कुछ पंक्तियों को लपेटते हैं।

एक आवरण बनाया प्रयोग की जाने वाली एक decoratorऔर context manager:


कार्यान्वयन

import inspect
from contextlib import contextmanager, ContextDecorator
import functools    

class wrap_exceptions(ContextDecorator):
    def __init__(self, wrapper_exc, *wrapped_exc):
        self.wrapper_exc = wrapper_exc
        self.wrapped_exc = wrapped_exc

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not exc_type:
            return
        try:
            raise exc_val
        except self.wrapped_exc:
            raise self.wrapper_exc from exc_val

    def __gen_wrapper(self, f, *args, **kwargs):
        with self:
            for res in f(*args, **kwargs):
                yield res

    def __call__(self, f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            with self:
                if inspect.isgeneratorfunction(f):
                    return self.__gen_wrapper(f, *args, **kw)
                else:
                    return f(*args, **kw)
        return wrapper

उपयोग के उदाहरण

डेकोरेटर

@wrap_exceptions(MyError, IndexError)
def do():
   pass

जब कॉलिंग doविधि IndexError, बस के बारे में चिंता मत करोMyError

try:
   do()
except MyError as my_err:
   pass # handle error 

संदर्भ प्रबंधक

def do2():
   print('do2')
   with wrap_exceptions(MyError, IndexError):
       do()

अंदर do2, अंदर , context managerअगर IndexErrorउठाया जाता है , तो इसे लपेटा और उठाया जाएगाMyError


1
कृपया बताएं कि "रैपिंग" मूल अपवाद के लिए क्या करेगी। आपके कोड का उद्देश्य क्या है, और यह किस व्यवहार को सक्षम करता है?
एलेक्सिस

@alexis - ने कुछ उदाहरण जोड़े, आशा है कि यह मदद करता है
Aaron_ab

-2

आपकी जरूरतों का सबसे कठोर समाधान यह होना चाहिए:

try:
     upload(file_id)
except Exception as upload_error:
     error_msg = "Your upload failed! File: " + file_id
     raise RuntimeError(error_msg, upload_error)

इस तरह आप बाद में अपने संदेश और अपलोड फ़ंक्शन द्वारा फेंकी गई विशिष्ट त्रुटि को प्रिंट कर सकते हैं


1
वह कैच छोड़ता है और फिर अपवाद वस्तु को फेंक देता है, इसलिए नहीं, यह प्रश्न की जरूरतों को पूरा नहीं करता है। सवाल यह है कि करने के लिए कहता रखने सब के साथ उपयोगी इसमें दी गई जानकारी मौजूदा अपवाद और अनुमति देते हैं कि एक ही अपवाद है,, ढेर अप प्रचार जारी रखने के लिए।
bignose
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.