@@ -1241,11 +1241,6 @@ def _importer(target):
12411241 return thing
12421242
12431243
1244- def _is_started (patcher ):
1245- # XXXX horrible
1246- return hasattr (patcher , 'is_local' )
1247-
1248-
12491244class _patch (object ):
12501245
12511246 attribute_name = None
@@ -1316,34 +1311,16 @@ def decorate_class(self, klass):
13161311 @contextlib .contextmanager
13171312 def decoration_helper (self , patched , args , keywargs ):
13181313 extra_args = []
1319- entered_patchers = []
1320- patching = None
1321-
1322- exc_info = tuple ()
1323- try :
1314+ with contextlib .ExitStack () as exit_stack :
13241315 for patching in patched .patchings :
1325- arg = patching .__enter__ ()
1326- entered_patchers .append (patching )
1316+ arg = exit_stack .enter_context (patching )
13271317 if patching .attribute_name is not None :
13281318 keywargs .update (arg )
13291319 elif patching .new is DEFAULT :
13301320 extra_args .append (arg )
13311321
13321322 args += tuple (extra_args )
13331323 yield (args , keywargs )
1334- except :
1335- if (patching not in entered_patchers and
1336- _is_started (patching )):
1337- # the patcher may have been started, but an exception
1338- # raised whilst entering one of its additional_patchers
1339- entered_patchers .append (patching )
1340- # Pass the exception to __exit__
1341- exc_info = sys .exc_info ()
1342- # re-raise the exception
1343- raise
1344- finally :
1345- for patching in reversed (entered_patchers ):
1346- patching .__exit__ (* exc_info )
13471324
13481325
13491326 def decorate_callable (self , func ):
@@ -1520,25 +1497,26 @@ def __enter__(self):
15201497
15211498 self .temp_original = original
15221499 self .is_local = local
1523- setattr (self .target , self .attribute , new_attr )
1524- if self .attribute_name is not None :
1525- extra_args = {}
1526- if self .new is DEFAULT :
1527- extra_args [self .attribute_name ] = new
1528- for patching in self .additional_patchers :
1529- arg = patching .__enter__ ()
1530- if patching .new is DEFAULT :
1531- extra_args .update (arg )
1532- return extra_args
1533-
1534- return new
1535-
1500+ self ._exit_stack = contextlib .ExitStack ()
1501+ try :
1502+ setattr (self .target , self .attribute , new_attr )
1503+ if self .attribute_name is not None :
1504+ extra_args = {}
1505+ if self .new is DEFAULT :
1506+ extra_args [self .attribute_name ] = new
1507+ for patching in self .additional_patchers :
1508+ arg = self ._exit_stack .enter_context (patching )
1509+ if patching .new is DEFAULT :
1510+ extra_args .update (arg )
1511+ return extra_args
1512+
1513+ return new
1514+ except :
1515+ if not self .__exit__ (* sys .exc_info ()):
1516+ raise
15361517
15371518 def __exit__ (self , * exc_info ):
15381519 """Undo the patch."""
1539- if not _is_started (self ):
1540- return
1541-
15421520 if self .is_local and self .temp_original is not DEFAULT :
15431521 setattr (self .target , self .attribute , self .temp_original )
15441522 else :
@@ -1553,9 +1531,9 @@ def __exit__(self, *exc_info):
15531531 del self .temp_original
15541532 del self .is_local
15551533 del self .target
1556- for patcher in reversed ( self .additional_patchers ):
1557- if _is_started ( patcher ):
1558- patcher .__exit__ (* exc_info )
1534+ exit_stack = self ._exit_stack
1535+ del self . _exit_stack
1536+ return exit_stack .__exit__ (* exc_info )
15591537
15601538
15611539 def start (self ):
@@ -1571,9 +1549,9 @@ def stop(self):
15711549 self ._active_patches .remove (self )
15721550 except ValueError :
15731551 # If the patch hasn't been started this will fail
1574- pass
1552+ return None
15751553
1576- return self .__exit__ ()
1554+ return self .__exit__ (None , None , None )
15771555
15781556
15791557
@@ -1873,9 +1851,9 @@ def stop(self):
18731851 _patch ._active_patches .remove (self )
18741852 except ValueError :
18751853 # If the patch hasn't been started this will fail
1876- pass
1854+ return None
18771855
1878- return self .__exit__ ()
1856+ return self .__exit__ (None , None , None )
18791857
18801858
18811859def _clear_dict (in_dict ):
0 commit comments