1
- """Utilities for with-statement contexts. See PEP 343."""
1
+ """Utilities for with-statement contexts. See PEP 343.
2
2
3
- import sys
4
- from collections import deque
5
- from functools import wraps
3
+ Original source code: https://hg.python.org/cpython/file/3.4/Lib/contextlib.py
6
4
7
- __all__ = ["contextmanager" , "closing" , "ContextDecorator" , "ExitStack" ,
8
- "redirect_stdout" , "suppress" ]
5
+ Not implemented:
6
+ - redirect_stdout;
7
+ - ExitStack.
9
8
9
+ """
10
10
11
11
class ContextDecorator (object ):
12
12
"A base class or mixin that enables context managers to work as decorators."
@@ -24,7 +24,6 @@ def _recreate_cm(self):
24
24
return self
25
25
26
26
def __call__ (self , func ):
27
- @wraps (func )
28
27
def inner (* args , ** kwds ):
29
28
with self ._recreate_cm ():
30
29
return func (* args , ** kwds )
@@ -37,16 +36,6 @@ class _GeneratorContextManager(ContextDecorator):
37
36
def __init__ (self , func , * args , ** kwds ):
38
37
self .gen = func (* args , ** kwds )
39
38
self .func , self .args , self .kwds = func , args , kwds
40
- # Issue 19330: ensure context manager instances have good docstrings
41
- doc = getattr (func , "__doc__" , None )
42
- if doc is None :
43
- doc = type (self ).__doc__
44
- self .__doc__ = doc
45
- # Unfortunately, this still doesn't provide good help output when
46
- # inspecting the created context manager instances, since pydoc
47
- # currently bypasses the instance docstring and shows the docstring
48
- # for the class instead.
49
- # See http://bugs.python.org/issue19404 for more details.
50
39
51
40
def _recreate_cm (self ):
52
41
# _GCM instances are one-shot context managers, so the
@@ -81,16 +70,6 @@ def __exit__(self, type, value, traceback):
81
70
# was passed to throw(). This prevents a StopIteration
82
71
# raised inside the "with" statement from being suppressed
83
72
return exc is not value
84
- except :
85
- # only re-raise if it's *not* the exception that was
86
- # passed to throw(), because __exit__() must not raise
87
- # an exception unless __exit__() itself failed. But throw()
88
- # has to raise the exception to signal propagation, so this
89
- # fixes the impedance mismatch between the throw() protocol
90
- # and the __exit__() protocol.
91
- #
92
- if sys .exc_info ()[1 ] is not value :
93
- raise
94
73
95
74
96
75
def contextmanager (func ):
@@ -121,7 +100,6 @@ def some_generator(<arguments>):
121
100
<cleanup>
122
101
123
102
"""
124
- @wraps (func )
125
103
def helper (* args , ** kwds ):
126
104
return _GeneratorContextManager (func , * args , ** kwds )
127
105
return helper
@@ -151,32 +129,6 @@ def __enter__(self):
151
129
def __exit__ (self , * exc_info ):
152
130
self .thing .close ()
153
131
154
- class redirect_stdout :
155
- """Context manager for temporarily redirecting stdout to another file
156
-
157
- # How to send help() to stderr
158
- with redirect_stdout(sys.stderr):
159
- help(dir)
160
-
161
- # How to write help() to a file
162
- with open('help.txt', 'w') as f:
163
- with redirect_stdout(f):
164
- help(pow)
165
- """
166
-
167
- def __init__ (self , new_target ):
168
- self ._new_target = new_target
169
- # We use a list of old targets to make this CM re-entrant
170
- self ._old_targets = []
171
-
172
- def __enter__ (self ):
173
- self ._old_targets .append (sys .stdout )
174
- sys .stdout = self ._new_target
175
- return self ._new_target
176
-
177
- def __exit__ (self , exctype , excinst , exctb ):
178
- sys .stdout = self ._old_targets .pop ()
179
-
180
132
181
133
class suppress :
182
134
"""Context manager to suppress specified exceptions
@@ -206,135 +158,3 @@ def __exit__(self, exctype, excinst, exctb):
206
158
#
207
159
# See http://bugs.python.org/issue12029 for more details
208
160
return exctype is not None and issubclass (exctype , self ._exceptions )
209
-
210
-
211
- # Inspired by discussions on http://bugs.python.org/issue13585
212
- class ExitStack (object ):
213
- """Context manager for dynamic management of a stack of exit callbacks
214
-
215
- For example:
216
-
217
- with ExitStack() as stack:
218
- files = [stack.enter_context(open(fname)) for fname in filenames]
219
- # All opened files will automatically be closed at the end of
220
- # the with statement, even if attempts to open files later
221
- # in the list raise an exception
222
-
223
- """
224
- def __init__ (self ):
225
- self ._exit_callbacks = deque ()
226
-
227
- def pop_all (self ):
228
- """Preserve the context stack by transferring it to a new instance"""
229
- new_stack = type (self )()
230
- new_stack ._exit_callbacks = self ._exit_callbacks
231
- self ._exit_callbacks = deque ()
232
- return new_stack
233
-
234
- def _push_cm_exit (self , cm , cm_exit ):
235
- """Helper to correctly register callbacks to __exit__ methods"""
236
- def _exit_wrapper (* exc_details ):
237
- return cm_exit (cm , * exc_details )
238
- _exit_wrapper .__self__ = cm
239
- self .push (_exit_wrapper )
240
-
241
- def push (self , exit ):
242
- """Registers a callback with the standard __exit__ method signature
243
-
244
- Can suppress exceptions the same way __exit__ methods can.
245
-
246
- Also accepts any object with an __exit__ method (registering a call
247
- to the method instead of the object itself)
248
- """
249
- # We use an unbound method rather than a bound method to follow
250
- # the standard lookup behaviour for special methods
251
- _cb_type = type (exit )
252
- try :
253
- exit_method = _cb_type .__exit__
254
- except AttributeError :
255
- # Not a context manager, so assume its a callable
256
- self ._exit_callbacks .append (exit )
257
- else :
258
- self ._push_cm_exit (exit , exit_method )
259
- return exit # Allow use as a decorator
260
-
261
- def callback (self , callback , * args , ** kwds ):
262
- """Registers an arbitrary callback and arguments.
263
-
264
- Cannot suppress exceptions.
265
- """
266
- def _exit_wrapper (exc_type , exc , tb ):
267
- callback (* args , ** kwds )
268
- # We changed the signature, so using @wraps is not appropriate, but
269
- # setting __wrapped__ may still help with introspection
270
- _exit_wrapper .__wrapped__ = callback
271
- self .push (_exit_wrapper )
272
- return callback # Allow use as a decorator
273
-
274
- def enter_context (self , cm ):
275
- """Enters the supplied context manager
276
-
277
- If successful, also pushes its __exit__ method as a callback and
278
- returns the result of the __enter__ method.
279
- """
280
- # We look up the special methods on the type to match the with statement
281
- _cm_type = type (cm )
282
- _exit = _cm_type .__exit__
283
- result = _cm_type .__enter__ (cm )
284
- self ._push_cm_exit (cm , _exit )
285
- return result
286
-
287
- def close (self ):
288
- """Immediately unwind the context stack"""
289
- self .__exit__ (None , None , None )
290
-
291
- def __enter__ (self ):
292
- return self
293
-
294
- def __exit__ (self , * exc_details ):
295
- received_exc = exc_details [0 ] is not None
296
-
297
- # We manipulate the exception state so it behaves as though
298
- # we were actually nesting multiple with statements
299
- frame_exc = sys .exc_info ()[1 ]
300
- def _fix_exception_context (new_exc , old_exc ):
301
- # Context may not be correct, so find the end of the chain
302
- while 1 :
303
- exc_context = new_exc .__context__
304
- if exc_context is old_exc :
305
- # Context is already set correctly (see issue 20317)
306
- return
307
- if exc_context is None or exc_context is frame_exc :
308
- break
309
- new_exc = exc_context
310
- # Change the end of the chain to point to the exception
311
- # we expect it to reference
312
- new_exc .__context__ = old_exc
313
-
314
- # Callbacks are invoked in LIFO order to match the behaviour of
315
- # nested context managers
316
- suppressed_exc = False
317
- pending_raise = False
318
- while self ._exit_callbacks :
319
- cb = self ._exit_callbacks .pop ()
320
- try :
321
- if cb (* exc_details ):
322
- suppressed_exc = True
323
- pending_raise = False
324
- exc_details = (None , None , None )
325
- except :
326
- new_exc_details = sys .exc_info ()
327
- # simulate the stack of exceptions by setting the context
328
- _fix_exception_context (new_exc_details [1 ], exc_details [1 ])
329
- pending_raise = True
330
- exc_details = new_exc_details
331
- if pending_raise :
332
- try :
333
- # bare "raise exc_details[1]" replaces our carefully
334
- # set-up context
335
- fixed_ctx = exc_details [1 ].__context__
336
- raise exc_details [1 ]
337
- except BaseException :
338
- exc_details [1 ].__context__ = fixed_ctx
339
- raise
340
- return received_exc and suppressed_exc
0 commit comments