2012-01-02 13 views
5

इस में हो सकता है डेबियन निचोड़ पर अजगर 2.6.6 (डिफ़ॉल्ट) के साथ है। निम्नलिखित पायथन कोड पर विचार करें।अपवाद संचालन जब त्रुटियों मुख्य कार्यक्रम में या सफाई

import sys 
try: 
    raise Exception("error in main") 
    pass 
except: 
    exc_info = sys.exc_info() 
finally: 
    try: 
     print "cleanup - always run" 
     raise Exception("error in cleanup") 
    except: 
     import traceback 
     print >> sys.stderr, "Error in cleanup" 
     traceback.print_exc() 
    if 'exc_info' in locals(): 
     raise exc_info[0], exc_info[1], exc_info[2] 

print "exited normally" 

प्राप्त त्रुटि

Error in cleanup 
Traceback (most recent call last): 
    File "<stdin>", line 10, in <module> 
Exception: error in cleanup 
cleanup - always run 
Traceback (most recent call last): 
    File "<stdin>", line 3, in <module> 
Exception: error in main 

विचार एक स्थिति है जहाँ या तो कुछ कोड या कि कोड की सफाई (जो हमेशा चलाया जाता है) या दोनों, एक त्रुटि देता है से निपटने के लिए है। इसके बारे में कुछ चर्चा है, उदाहरण के लिए, Re-raising Exceptions में इयान बेकिंग द्वारा। उस पोस्ट के अंत में, (Update: देखें) उनके द्वारा वर्णित कोड + रोलबैक/वापस लाएं के इसी तरह के मामले को संभालने के लिए कैसे (केवल गलती की स्थिति में आ जाते है)।

मैंने इसके साथ झुकाया और उपरोक्त कोड के साथ आया, जो एक राक्षसीता है। विशेष रूप से, यदि केवल क्लीनअप में एक त्रुटि (raise Exception("error in main") पर टिप्पणी कर रहा है), कोड अभी भी सामान्य रूप से बाहर निकलता है, हालांकि यह एक ट्रेसबैक प्रिंट करता है। वर्तमान में, मैं गैर-सफाई त्रुटि प्राथमिकता दे रहा हूं, इसलिए यह प्रोग्राम को रोकता है।

आदर्श रूप में मैं प्रोग्राम को रोकने के लिए या तो त्रुटि करना चाहता हूं, लेकिन ऐसा व्यवस्थित करना आसान नहीं लगता है। पाइथन केवल एक त्रुटि उठाना चाहता है, अगर किसी को भी खो देता है, और डिफ़ॉल्ट रूप से यह आमतौर पर आखिरी है। इससे पुन: प्रयास करने से ऊपर की तरह संकल्प बढ़ जाता है।

इसके अलावा locals() का उपयोग थोड़ा बदसूरत है। क्या कोई बेहतर कर सकता है?

संपादित करें: srgerg's answer ने मुझे संदर्भ प्रबंधकों और with कीवर्ड की धारणा के साथ पेश किया। PEP 343 के अलावा, प्रलेखन के अन्य प्रासंगिक बिट मैं (किसी विशेष क्रम में) कर रहे हैं पाया। Context Manager Types, The with statement, और http://docs.python.org/reference/datamodel.html#context-managers। यह निश्चित रूप से इस के लिए पिछले दृष्टिकोणों से, यानी स्पेगेटी कोड trys, excepts, और finallys को शामिल पर एक बड़ा सुधार की तरह लगता है।

संक्षेप में, वहाँ दो चीजें हैं जो मैं इस तरह के एक समाधान चाहते हैं मुझे देने के लिए कर रहे हैं।

  1. या तो मुख्य कोड में या सफाई में एक अपवाद के लिए क्षमता ने रास्ते में ही कार्यक्रम को रोकने के लिए। संदर्भ प्रबंधक यह करते हैं, क्योंकि यदि लूप के शरीर में अपवाद है और का शरीर बाहर निकलें नहीं है, तो उस अपवाद का प्रचार किया जाता है। यदि बाहर निकलें एक अपवाद फेंकता है और लूप के साथ शरीर नहीं है तो प्रचारित किया जाता है। यदि दोनों एक अपवाद फेंकते हैं, तो बाहर निकलें अपवाद प्रसारित किया जाता है और जबकि लूप के शरीर से एक दबाया जाता है। यह सब, Context Manager Types से अर्थात प्रलेखित है

    contextmanager। बाहर निकलें (exc_type, exc_val, exc_tb)

    रनटाइम संदर्भ से बाहर निकलें और एक बूलियन ध्वज लौटाएं जो दर्शाता है कि क्या हुआ कोई अपवाद दबाया जाना चाहिए। [...] इस विधि से एक वास्तविक मूल्य लौटने से बयान के साथ अपवाद को दबाने और कथन के साथ तुरंत कथन के बाद निष्पादन जारी रहेगा। अन्यथा इस विधि के बाद निष्पादन समाप्त होने के बाद अपवाद जारी है। इस विधि के निष्पादन के दौरान होने वाली अपवाद
    कथन के साथ किसी भी अपवाद को प्रतिस्थापित करेंगे। [...] पारित अपवाद को स्पष्ट रूप से कभी नहीं बदला जाना चाहिए। इसके बजाए, इस विधि को पर झूठा मान वापस करना चाहिए, यह इंगित करता है कि विधि सफलतापूर्वक पूर्ण हो गई है और उठाए गए अपवाद को दबाना नहीं चाहता है।

  2. वहाँ दोनों स्थानों में अपवाद हैं, तो मैं ट्रेस बैक दोनों से को देखने के लिए, भले ही तकनीकी रूप से केवल एक अपवाद फेंक दिया जाता है चाहता हूँ। इसका कारण यह है कि दोनों एक अपवाद है, तो बाहर निकलने अपवाद प्रचारित किया जाता है फेंक, प्रयोग के आधार पर सही है, लेकिन जबकि पाश के शरीर से ट्रैस बैक अभी भी srgerg's answer के रूप में, प्रिंट होता है। हालांकि, मुझे यह दस्तावेज कहीं भी नहीं मिला है, जो असंतोषजनक है।

    import sys, traceback 
    
    def excepthook(*exc_info): 
        print "cleanup - always run" 
        raise Exception("error in cleanup") 
        traceback.print_exception(*exc_info) 
    sys.excepthook = excepthook 
    
    raise Exception("error in main") 
    

    उदाहरण आउटपुट:

उत्तर

4

आदर्श रूप में, आप try ... except ब्लॉक के भीतर सफाई को संभालने के लिए अजगर with statement का उपयोग करेंगे, जो कुछ इस तरह दिखेगा::

class Something(object): 
    def __enter__(self): 
     print "Entering" 

    def __exit__(self, t, v, tr): 
     print "cleanup - always runs" 
     raise Exception("Exception occurred during __exit__") 

try: 
    with Something() as something: 
     raise Exception("Exception occurred!") 
except Exception, e: 
    print e 
    import traceback 
    traceback.print_exc(e) 

print "Exited normally!" 
विशेष रूप से, CPython में मैं निम्नलिखित कार्यान्वयन देखा है

जब मैं इस चलाने के लिए, यह प्रिंट:

Entering 
cleanup - always runs 
Exception occurred during __exit__ 
Traceback (most recent call last): 
    File "s3.py", line 11, in <module> 
    raise Exception("Exception occurred!") 
    File "s3.py", line 7, in __exit__ 
    raise Exception("Exception occurred during __exit__") 
Exception: Exception occurred during __exit__ 
Exited normally! 

ध्यान दें, या तो अपवाद कार्यक्रम समाप्त हो जाएगी, औरमें से निपटा जा सकताकथन।

संपादित करें: ऊपर लिंक किए गए बयान के साथ प्रलेखन के अनुसार, __exit__() विधि केवल एक अपवाद उठाना चाहिए अगर वहाँ __exit__() अंदर एक त्रुटि है - जो है, यह नहीं अपवाद यह में पारित कर दिया फिर से उठाना चाहिए।

यह समस्या है यदि with कथन और __exit__() विधि दोनों कोड एक अपवाद उठाते हैं। उस स्थिति में, छोड़कर खंड में पकड़ा गया अपवाद __exit__() में उठाया गया है। यदि आप कथन से में उठाया एक चाहते हैं, आप कुछ इस तरह कर सकते हैं:

class Something(object): 
    def __enter__(self): 
     print "Entering" 

    def __exit__(self, t, v, tr): 
     print "cleanup - always runs" 
     try: 
      raise Exception("Exception occurred during __exit__") 
     except Exception, e: 
      if (t, v, tr) != (None, None, None): 
       # __exit__ called with an existing exception 
       return False 
      else: 
       # __exit__ called with NO existing exception 
       raise 

try: 
    with Something() as something: 
     raise Exception("Exception occurred!") 
     pass 
except Exception, e: 
    print e 
    traceback.print_exc(e) 
    raise 

print "Exited normally!" 

यह प्रिंट:

Entering 
cleanup - always runs 
Exception occurred! 
Traceback (most recent call last): 
    File "s2.py", line 22, in <module> 
    raise Exception("Exception occurred!") 
Exception: Exception occurred! 
Traceback (most recent call last): 
    File "s2.py", line 22, in <module> 
    raise Exception("Exception occurred!") 
Exception: Exception occurred! 
+0

धन्यवाद, यह मेरे लिए एक बिल्कुल नया विचार है। क्या विचार साफ है जहां आपने अपवाद बढ़ाया है ("अपवाद __exit __ के दौरान हुआ") '? यदि हां, तो क्या आप एक उचित 'प्रिंट "क्लीनअप जोड़ सकते हैं - हमेशा" 'या इसी तरह से चलाएं? –

+0

मैंने अनुरोध के रूप में प्रिंट स्टेटमेंट जोड़ा है। – srgerg

+0

धन्यवाद, srgerg। मैं पीईपी पढ़ रहा हूँ। मुझे नहीं लगता कि मैंने पहले 'कीवर्ड' के बारे में सुना या देखा था, लेकिन हो सकता है कि मैं सिर्फ ध्यान नहीं दे रहा था। क्या कोई व्यक्ति प्रवेश या निकास समारोह में क्लीनअप कोड डाल देगा, या इससे कोई फर्क नहीं पड़ता है? –

1

एक समान व्यवहार एक स्वनिर्धारित exception hook प्रदान करके प्राप्त किया जा सकता इस उदाहरण कोड के रूप में काम करता है में

cleanup - always run 
Error in sys.excepthook: 
Traceback (most recent call last): 
    File "test.py", line 5, in excepthook 
    raise Exception("error in cleanup") 
Exception: error in cleanup 

Original exception was: 
Traceback (most recent call last): 
    File "test.py", line 9, in <module> 
    raise Exception("error in main") 
Exception: error in main 

इस प्रकार है:

  • यदि कोई अपवाद नहीं है ' टी पकड़ा, excepthook निष्पादित किया गया है।
  • अपवाद प्रिंट करने से पहले, excepthook कुछ क्लीनअप कोड चलाता है (जो मूल प्रश्न में finally के तहत था)।
  • यदि हुक में कोई अपवाद उठाया जाता है, तो उस अपवाद को मुद्रित किया जाता है और उसके बाद, मूल अपवाद भी मुद्रित होता है।

नोट: मैं मूल अपवाद है जब कुछ हुक में विफल रहता है के मुद्रण के बारे में कोई प्रलेखन नहीं मिला है, लेकिन मैं दोनों CPython और Jython में इस व्यवहार को देखा है।

void 
PyErr_PrintEx(int set_sys_last_vars) 
{ 
    ... 
    hook = PySys_GetObject("excepthook"); 
    if (hook) { 
     ... 
     if (result == NULL) { 
      ... 
      PySys_WriteStderr("Error in sys.excepthook:\n"); 
      PyErr_Display(exception2, v2, tb2); 
      PySys_WriteStderr("\nOriginal exception was:\n"); 
      PyErr_Display(exception, v, tb); 
      ... 
     } 
    } 
} 
+0

धन्यवाद, लेकिन मैं स्पष्ट नहीं है कि यह कैसे मेरी स्क्रिप्ट के संदर्भ में काम करेगा हूँ। क्या आप एक समान उदाहरण, या शायद एक ही उदाहरण के साथ चित्रित कर सकते हैं? –

+0

@ फेहेम मिथा मैंने अधिक जानकारी जोड़ने के लिए अपना जवाब संपादित कर लिया है। अगर मैं सही ढंग से समझ गया, तो आपके प्रश्न में और मेरे जवाब में उदाहरण वास्तव में वही है। – jcollado

+0

धन्यवाद, आपकी व्याख्या ने चीजों को स्पष्ट करने में मदद की। ऐसा लगता है कि सभी अपवादों के लिए बहिष्कार चलाया गया है जो दायरे में हैं, है ना? मैं इसे एक विशेष संदर्भ में कैसे स्थानांतरित कर सकता हूं? मैं नहीं चाहता कि यह सभी परिस्थितियों में चलें। –

0

आप बहुत एक सरल उपाय के करीब थे। पहले अपवाद में बस traceback.print_exc() का उपयोग करें-फिर आपको दूसरे अपवाद को संभालने की आवश्यकता नहीं है।

error7 = False 
try: 
    raise Exception("error in main") 
    pass 
except: 
    import traceback 
    traceback.print_exc() 
    error7 = True 
finally: 
    print "cleanup - always run" 
    raise Exception("error in cleanup") 
    if error7: 
     raise SystemExit() 

print "exited normally" 

जानकारी एक अपवाद उत्पन्न हुआ था error7 में संग्रहित है और यदि यह मामला था, SystemExit()finally ब्लॉक के अंत में उठाया है कि क्या: यहाँ है कि दिखाई दे सकता है क्या। दोनों raise बयान को सक्षम करने पर

आउटपुट:

cleanup - always run 
Traceback (most recent call last): 
    File "G:/backed-up to mozy/Scripts/sandbox.py", line 3, in <module> 
    raise Exception("error in main") 
Exception: error in main 
Traceback (most recent call last): 

    File "<ipython-input-1-10089b43dd14>", line 1, in <module> 
    runfile('G:/backed-up to mozy/Scripts/sandbox.py', wdir='G:/backed-up to mozy/Scripts') 

    File "C:\Anaconda2\lib\site-packages\spyder\utils\site\sitecustomize.py", line 866, in runfile 
    execfile(filename, namespace) 

    File "C:\Anaconda2\lib\site-packages\spyder\utils\site\sitecustomize.py", line 87, in execfile 
    exec(compile(scripttext, filename, 'exec'), glob, loc) 

    File "G:/backed-up to mozy/Scripts/sandbox.py", line 11, in <module> 
    raise Exception("error in cleanup") 

Exception: error in cleanup 
संबंधित मुद्दे