2012-10-25 17 views
16

क्या यह सुनिश्चित करना संभव है कि __exit__() विधि को __enter__() में अपवाद होने पर भी यह सुनिश्चित किया जा सके?संदर्भ प्रबंधक में अपवाद को पकड़ना __enter __()

>>> class TstContx(object): 
... def __enter__(self): 
...  raise Exception('Oops in __enter__') 
... 
... def __exit__(self, e_typ, e_val, trcbak): 
...  print "This isn't running" 
... 
>>> with TstContx(): 
...  pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in __enter__ 
Exception: Oops in __enter__ 
>>> 

संपादित

यह मैं मिल सकता है के रूप में के रूप में करीब है ...

class TstContx(object): 
    def __enter__(self): 
     try: 
      # __enter__ code 
     except Exception as e 
      self.init_exc = e 

     return self 

    def __exit__(self, e_typ, e_val, trcbak): 
     if all((e_typ, e_val, trcbak)): 
      raise e_typ, e_val, trcbak 

     # __exit__ code 


with TstContx() as tc: 
    if hasattr(tc, 'init_exc'): raise tc.init_exc 

    # code in context 

हिंद दृष्टि में, एक संदर्भ प्रबंधक सबसे अच्छा डिजाइन निर्णय

+4

समस्या यह है कि '__enter__' के भीतर से 'शरीर' को छोड़ना संभव नहीं है (देखें [पेप 377] (http://www.python.org/dev/peps/pep-0377/)) – georg

उत्तर

14
इस तरह

:

import sys 

class Context(object): 
    def __enter__(self): 
     try: 
      raise Exception("Oops in __enter__") 
     except: 
      # Swallow exception if __exit__ returns a True value 
      if self.__exit__(*sys.exc_info()): 
       pass 
      else: 
       raise 


    def __exit__(self, e_typ, e_val, trcbak): 
     print "Now it's running" 


with Context(): 
    pass 

कार्यक्रम संदर्भ ब्लॉक आप संदर्भ ब्लॉक के अंदर संदर्भ वस्तु का निरीक्षण करने की जरूरत है और केवल महत्वपूर्ण सामग्री करते हैं __enter__ सफल क्रियान्वित करने के बिना अपनी प्रमुदित रास्ते पर जारी रखने के लिए जाने के लिए।

class Context(object): 
    def __init__(self): 
     self.enter_ok = True 

    def __enter__(self): 
     try: 
      raise Exception("Oops in __enter__") 
     except: 
      if self.__exit__(*sys.exc_info()): 
       self.enter_ok = False 
      else: 
       raise 
     return self 

    def __exit__(self, e_typ, e_val, trcbak): 
     print "Now this runs twice" 
     return True 


with Context() as c: 
    if c.enter_ok: 
     print "Only runs if enter succeeded" 

print "Execution continues" 

जहां तक ​​मैं निर्धारित कर सकता हूं, आप पूरी तरह से ब्लॉक को छोड़ नहीं सकते हैं। और ध्यान दें कि यह संदर्भ अब सभी अपवादों को निगलता है। यदि आप __enter__ सफल होने पर अपवादों को निगलना नहीं चाहते हैं, तो __exit__ और return False में True देखें।

+1

यदि '__enter__' में एक अपवाद है और आप '__exit__' कहते हैं, क्या क्लाइंट कोड में' with' ब्लॉक से बाहर निकलने का कोई तरीका है? – tMC

+0

@tMC अद्यतन उत्तर देखें। –

+0

lol मैंने बस उसी समय के बारे में सोचा था। मैंने एक ही तर्क के साथ अपना प्रश्न अपडेट किया। – tMC

6
नहीं गया हो सकता है

नहीं। अगर __enter__() में कोई अपवाद हो सकता है तो आपको इसे स्वयं पकड़ना होगा और एक सहायक को कॉल करना होगा फ़ंक्शन जिसमें क्लीनअप कोड होता है।

2

आप इस्तेमाल कर सकते हैं contextlib.ExitStack (परीक्षण नहीं):

with ExitStack() as stack: 
    cm = TstContx() 
    stack.push(cm) # ensure __exit__ is called 
    with ctx: 
     stack.pop_all() # __enter__ succeeded, don't call __exit__ callback 

या the docs से एक उदाहरण:

stack = ExitStack() 
try: 
    x = stack.enter_context(cm) 
except Exception: 
    # handle __enter__ exception 
else: 
    with stack: 
     # Handle normal case 

contextlib2 on Python <3.3 देखें।

from contextlib import contextmanager 

@contextmanager 
def test_cm(): 
    try: 
     # dangerous code 
     yield 
    except Exception, err 
     pass # do something 
1

अगर विरासत या जटिल सबरूटीन्स की आवश्यकता नहीं है, तो आप एक छोटे तरीके से उपयोग कर सकते हैं। यह तर्क के रूप में त्रुटि के साथ __exit __() को कॉल करता है। अगर तर्क [0] में कोई त्रुटि होती है तो यह क्लीन अप कोड निष्पादित करने के बाद अपवाद को पुनः प्राप्त करती है।

+1

हां, लेकिन यह संदर्भ में "जेनरेटर उपज नहीं" फेंक देगा। – georg

+0

@ thg435, उचित, लेकिन हम कोशिश के साथ 'उपज' को लपेट सकते हैं ... आखिरकार – newtover

+0

जो आखिरकार – newtover

0
class MyContext: 
    def __enter__(self): 
     try: 
      pass 
      # exception-raising code 
     except Exception as e: 
      self.__exit__(e) 

    def __exit__(self, *args): 
     # clean up code ... 
     if args[0]: 
      raise 

मैं इसे इस तरह से किया है:

संबंधित मुद्दे