@@ -1229,11 +1229,6 @@ def _importer(target):
12291229 return thing
12301230
12311231
1232- def _is_started (patcher ):
1233- # XXXX horrible
1234- return hasattr (patcher , 'is_local' )
1235-
1236-
12371232class _patch (object ):
12381233
12391234 attribute_name = None
@@ -1304,34 +1299,16 @@ def decorate_class(self, klass):
13041299 @contextlib .contextmanager
13051300 def decoration_helper (self , patched , args , keywargs ):
13061301 extra_args = []
1307- entered_patchers = []
1308- patching = None
1309-
1310- exc_info = tuple ()
1311- try :
1302+ with contextlib .ExitStack () as exit_stack :
13121303 for patching in patched .patchings :
1313- arg = patching .__enter__ ()
1314- entered_patchers .append (patching )
1304+ arg = exit_stack .enter_context (patching )
13151305 if patching .attribute_name is not None :
13161306 keywargs .update (arg )
13171307 elif patching .new is DEFAULT :
13181308 extra_args .append (arg )
13191309
13201310 args += tuple (extra_args )
13211311 yield (args , keywargs )
1322- except :
1323- if (patching not in entered_patchers and
1324- _is_started (patching )):
1325- # the patcher may have been started, but an exception
1326- # raised whilst entering one of its additional_patchers
1327- entered_patchers .append (patching )
1328- # Pass the exception to __exit__
1329- exc_info = sys .exc_info ()
1330- # re-raise the exception
1331- raise
1332- finally :
1333- for patching in reversed (entered_patchers ):
1334- patching .__exit__ (* exc_info )
13351312
13361313
13371314 def decorate_callable (self , func ):
@@ -1508,25 +1485,26 @@ def __enter__(self):
15081485
15091486 self .temp_original = original
15101487 self .is_local = local
1511- setattr (self .target , self .attribute , new_attr )
1512- if self .attribute_name is not None :
1513- extra_args = {}
1514- if self .new is DEFAULT :
1515- extra_args [self .attribute_name ] = new
1516- for patching in self .additional_patchers :
1517- arg = patching .__enter__ ()
1518- if patching .new is DEFAULT :
1519- extra_args .update (arg )
1520- return extra_args
1521-
1522- return new
1523-
1488+ self ._exit_stack = contextlib .ExitStack ()
1489+ try :
1490+ setattr (self .target , self .attribute , new_attr )
1491+ if self .attribute_name is not None :
1492+ extra_args = {}
1493+ if self .new is DEFAULT :
1494+ extra_args [self .attribute_name ] = new
1495+ for patching in self .additional_patchers :
1496+ arg = self ._exit_stack .enter_context (patching )
1497+ if patching .new is DEFAULT :
1498+ extra_args .update (arg )
1499+ return extra_args
1500+
1501+ return new
1502+ except :
1503+ if not self .__exit__ (* sys .exc_info ()):
1504+ raise
15241505
15251506 def __exit__ (self , * exc_info ):
15261507 """Undo the patch."""
1527- if not _is_started (self ):
1528- return
1529-
15301508 if self .is_local and self .temp_original is not DEFAULT :
15311509 setattr (self .target , self .attribute , self .temp_original )
15321510 else :
@@ -1541,9 +1519,9 @@ def __exit__(self, *exc_info):
15411519 del self .temp_original
15421520 del self .is_local
15431521 del self .target
1544- for patcher in reversed ( self .additional_patchers ):
1545- if _is_started ( patcher ):
1546- patcher .__exit__ (* exc_info )
1522+ exit_stack = self ._exit_stack
1523+ del self . _exit_stack
1524+ return exit_stack .__exit__ (* exc_info )
15471525
15481526
15491527 def start (self ):
@@ -1559,9 +1537,9 @@ def stop(self):
15591537 self ._active_patches .remove (self )
15601538 except ValueError :
15611539 # If the patch hasn't been started this will fail
1562- pass
1540+ return None
15631541
1564- return self .__exit__ ()
1542+ return self .__exit__ (None , None , None )
15651543
15661544
15671545
0 commit comments