Skip to content

Commit 789b58b

Browse files
Address picnixz review: discard deferred error on any exceptional exit
1 parent 157fd46 commit 789b58b

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

‎Lib/_pydecimal.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4165,16 +4165,18 @@ def _raise_error(self, condition, explanation = None, *args):
41654165
def _defer_traps(self):
41664166
"""Defer trapped signals until the end of the with block.
41674167
4168-
_raise_error sets the flag of every signal in the block and
4168+
_raise_error() sets the flag of every signal in the block and
41694169
raises the first trapped exception on exit, so that a
41704170
multi-signal operation sets the same flags as _decimal.
41714171
"""
41724172
self._deferred_traps += 1
41734173
try:
41744174
yield
41754175
except BaseException:
4176-
if self._deferred_traps == 1:
4177-
self._deferred_error = None
4176+
# The block is aborted, so the deferred error is
4177+
# discarded: the propagating exception wins, and the
4178+
# context does not retain an exception object.
4179+
self._deferred_error = None
41784180
raise
41794181
finally:
41804182
self._deferred_traps -= 1

‎Lib/test/test_decimal.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4311,6 +4311,32 @@ def test_deferred_traps_attributes(self):
43114311
self.assertEqual(context._deferred_traps, 0)
43124312
self.assertIsNone(context._deferred_error)
43134313

4314+
def test_deferred_traps_exception_discards_error(self):
4315+
# If an exception propagates through a _defer_traps block, the
4316+
# deferred error is discarded at every nesting level and the
4317+
# state is reset, so the propagating exception wins and the
4318+
# context stays usable.
4319+
Context = self.decimal.Context
4320+
Decimal = self.decimal.Decimal
4321+
Overflow = self.decimal.Overflow
4322+
context = Context(prec=9, Emin=-999, Emax=999, traps=[Overflow])
4323+
with self.assertRaises(RuntimeError):
4324+
with context._defer_traps():
4325+
with context._defer_traps():
4326+
context._raise_error(Overflow, 'test', 1)
4327+
raise RuntimeError('boom')
4328+
self.assertEqual(context._deferred_traps, 0)
4329+
self.assertIsNone(context._deferred_error)
4330+
4331+
# The flag was still set, and a later trapped operation
4332+
# raises its own error rather than the discarded one.
4333+
self.assertTrue(context.flags[Overflow])
4334+
context.clear_flags()
4335+
with self.assertRaises(Overflow):
4336+
context.multiply(Decimal('1E+800'), Decimal('1E+800'))
4337+
self.assertEqual(context._deferred_traps, 0)
4338+
self.assertIsNone(context._deferred_error)
4339+
43144340
class SpecialContexts:
43154341
"""Test the context templates."""
43164342

0 commit comments

Comments
 (0)