| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | """Utilities for with-statement contexts.  See PEP 343.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2008-04-30 21:08:42 +00:00
										 |  |  | from functools import wraps | 
					
						
							| 
									
										
										
										
											2009-05-29 01:46:48 +00:00
										 |  |  | from warnings import warn | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-03 13:02:47 +00:00
										 |  |  | __all__ = ["contextmanager", "nested", "closing"] | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-03 13:02:47 +00:00
										 |  |  | class GeneratorContextManager(object): | 
					
						
							|  |  |  |     """Helper for @contextmanager decorator.""" | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, gen): | 
					
						
							|  |  |  |         self.gen = gen | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return self.gen.next() | 
					
						
							|  |  |  |         except StopIteration: | 
					
						
							|  |  |  |             raise RuntimeError("generator didn't yield") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, type, value, traceback): | 
					
						
							|  |  |  |         if type is None: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.gen.next() | 
					
						
							|  |  |  |             except StopIteration: | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise RuntimeError("generator didn't stop") | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-11-02 10:09:12 +00:00
										 |  |  |             if value is None: | 
					
						
							|  |  |  |                 # Need to force instantiation so we can reliably | 
					
						
							|  |  |  |                 # tell if we get the same exception back | 
					
						
							|  |  |  |                 value = type() | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 self.gen.throw(type, value, traceback) | 
					
						
							| 
									
										
										
										
											2006-03-25 00:28:24 +00:00
										 |  |  |                 raise RuntimeError("generator didn't stop after throw()") | 
					
						
							| 
									
										
										
										
											2006-04-10 17:56:29 +00:00
										 |  |  |             except StopIteration, exc: | 
					
						
							| 
									
										
										
										
											2006-04-03 21:20:07 +00:00
										 |  |  |                 # Suppress the exception *unless* it's the same exception that | 
					
						
							|  |  |  |                 # was passed to throw().  This prevents a StopIteration | 
					
						
							|  |  |  |                 # raised inside the "with" statement from being suppressed | 
					
						
							| 
									
										
										
										
											2006-04-10 17:56:29 +00:00
										 |  |  |                 return exc is not value | 
					
						
							| 
									
										
										
										
											2006-03-25 00:28:24 +00:00
										 |  |  |             except: | 
					
						
							| 
									
										
										
										
											2006-03-25 04:32:12 +00:00
										 |  |  |                 # only re-raise if it's *not* the exception that was | 
					
						
							|  |  |  |                 # passed to throw(), because __exit__() must not raise | 
					
						
							|  |  |  |                 # an exception unless __exit__() itself failed.  But throw() | 
					
						
							|  |  |  |                 # has to raise the exception to signal propagation, so this | 
					
						
							|  |  |  |                 # fixes the impedance mismatch between the throw() protocol | 
					
						
							|  |  |  |                 # and the __exit__() protocol. | 
					
						
							|  |  |  |                 # | 
					
						
							| 
									
										
										
										
											2006-03-25 00:28:24 +00:00
										 |  |  |                 if sys.exc_info()[1] is not value: | 
					
						
							|  |  |  |                     raise | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-03 13:02:47 +00:00
										 |  |  | def contextmanager(func): | 
					
						
							|  |  |  |     """@contextmanager decorator.
 | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Typical usage: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-03 13:02:47 +00:00
										 |  |  |         @contextmanager | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |         def some_generator(<arguments>): | 
					
						
							|  |  |  |             <setup> | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 yield <value> | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 <cleanup> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This makes this: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with some_generator(<arguments>) as <variable>: | 
					
						
							|  |  |  |             <body> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     equivalent to this: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         <setup> | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             <variable> = <value> | 
					
						
							|  |  |  |             <body> | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             <cleanup> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2008-04-30 21:08:42 +00:00
										 |  |  |     @wraps(func) | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |     def helper(*args, **kwds): | 
					
						
							| 
									
										
										
										
											2006-05-03 13:02:47 +00:00
										 |  |  |         return GeneratorContextManager(func(*args, **kwds)) | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |     return helper | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-03 13:02:47 +00:00
										 |  |  | @contextmanager | 
					
						
							| 
									
										
										
										
											2006-05-02 19:47:52 +00:00
										 |  |  | def nested(*managers): | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |     """Support multiple context managers in a single with-statement.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Code like this: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with nested(A, B, C) as (X, Y, Z): | 
					
						
							|  |  |  |             <body> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     is equivalent to this: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with A as X: | 
					
						
							|  |  |  |             with B as Y: | 
					
						
							|  |  |  |                 with C as Z: | 
					
						
							|  |  |  |                     <body> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2009-05-29 01:46:48 +00:00
										 |  |  |     warn("With-statements now directly support multiple context managers", | 
					
						
							|  |  |  |          DeprecationWarning, 2) | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |     exits = [] | 
					
						
							|  |  |  |     vars = [] | 
					
						
							| 
									
										
										
										
											2006-03-10 02:28:35 +00:00
										 |  |  |     exc = (None, None, None) | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2007-08-23 18:11:33 +00:00
										 |  |  |         for mgr in managers: | 
					
						
							|  |  |  |             exit = mgr.__exit__ | 
					
						
							|  |  |  |             enter = mgr.__enter__ | 
					
						
							|  |  |  |             vars.append(enter()) | 
					
						
							|  |  |  |             exits.append(exit) | 
					
						
							|  |  |  |         yield vars | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         exc = sys.exc_info() | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         while exits: | 
					
						
							|  |  |  |             exit = exits.pop() | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2006-03-10 02:28:35 +00:00
										 |  |  |                 if exit(*exc): | 
					
						
							|  |  |  |                     exc = (None, None, None) | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  |             except: | 
					
						
							|  |  |  |                 exc = sys.exc_info() | 
					
						
							|  |  |  |         if exc != (None, None, None): | 
					
						
							| 
									
										
										
										
											2006-04-24 04:37:15 +00:00
										 |  |  |             # Don't rely on sys.exc_info() still containing | 
					
						
							|  |  |  |             # the right information. Another exception may | 
					
						
							|  |  |  |             # have been raised and caught by an exit method | 
					
						
							|  |  |  |             raise exc[0], exc[1], exc[2] | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-04-25 10:56:51 +00:00
										 |  |  | class closing(object): | 
					
						
							|  |  |  |     """Context to automatically close something at the end of a block.
 | 
					
						
							| 
									
										
										
										
											2006-02-28 21:57:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Code like this: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with closing(<module>.open(<arguments>)) as f: | 
					
						
							|  |  |  |             <block> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     is equivalent to this: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f = <module>.open(<arguments>) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             <block> | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             f.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2006-04-25 10:56:51 +00:00
										 |  |  |     def __init__(self, thing): | 
					
						
							|  |  |  |         self.thing = thing | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self.thing | 
					
						
							|  |  |  |     def __exit__(self, *exc_info): | 
					
						
							|  |  |  |         self.thing.close() |