@@ -1229,11 +1229,6 @@ def _importer(target):
1229
1229
return thing
1230
1230
1231
1231
1232
- def _is_started (patcher ):
1233
- # XXXX horrible
1234
- return hasattr (patcher , 'is_local' )
1235
-
1236
-
1237
1232
class _patch (object ):
1238
1233
1239
1234
attribute_name = None
@@ -1304,34 +1299,16 @@ def decorate_class(self, klass):
1304
1299
@contextlib .contextmanager
1305
1300
def decoration_helper (self , patched , args , keywargs ):
1306
1301
extra_args = []
1307
- entered_patchers = []
1308
- patching = None
1309
-
1310
- exc_info = tuple ()
1311
- try :
1302
+ with contextlib .ExitStack () as exit_stack :
1312
1303
for patching in patched .patchings :
1313
- arg = patching .__enter__ ()
1314
- entered_patchers .append (patching )
1304
+ arg = exit_stack .enter_context (patching )
1315
1305
if patching .attribute_name is not None :
1316
1306
keywargs .update (arg )
1317
1307
elif patching .new is DEFAULT :
1318
1308
extra_args .append (arg )
1319
1309
1320
1310
args += tuple (extra_args )
1321
1311
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 )
1335
1312
1336
1313
1337
1314
def decorate_callable (self , func ):
@@ -1508,25 +1485,26 @@ def __enter__(self):
1508
1485
1509
1486
self .temp_original = original
1510
1487
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
1524
1505
1525
1506
def __exit__ (self , * exc_info ):
1526
1507
"""Undo the patch."""
1527
- if not _is_started (self ):
1528
- return
1529
-
1530
1508
if self .is_local and self .temp_original is not DEFAULT :
1531
1509
setattr (self .target , self .attribute , self .temp_original )
1532
1510
else :
@@ -1541,9 +1519,9 @@ def __exit__(self, *exc_info):
1541
1519
del self .temp_original
1542
1520
del self .is_local
1543
1521
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 )
1547
1525
1548
1526
1549
1527
def start (self ):
@@ -1559,9 +1537,9 @@ def stop(self):
1559
1537
self ._active_patches .remove (self )
1560
1538
except ValueError :
1561
1539
# If the patch hasn't been started this will fail
1562
- pass
1540
+ return None
1563
1541
1564
- return self .__exit__ ()
1542
+ return self .__exit__ (None , None , None )
1565
1543
1566
1544
1567
1545
0 commit comments