6262import math as _math
6363import numbers as _numbers
6464import sys
65+ from contextlib import contextmanager as _contextmanager
6566
6667try :
6768 from collections import namedtuple as _namedtuple
@@ -1499,8 +1500,10 @@ def __divmod__(self, other, context=None):
14991500 ans = context ._raise_error (DivisionUndefined , 'divmod(0, 0)' )
15001501 return ans , ans
15011502 else :
1502- return (context ._raise_error (DivisionByZero , 'x // 0' , sign ),
1503- context ._raise_error (InvalidOperation , 'x % 0' ))
1503+ with context ._defer_traps ():
1504+ return (context ._raise_error (DivisionByZero , 'x // 0' ,
1505+ sign ),
1506+ context ._raise_error (InvalidOperation , 'x % 0' ))
15041507
15051508 quotient , remainder = self ._divide (other , context )
15061509 remainder = remainder ._fix (context )
@@ -1746,9 +1749,10 @@ def _fix(self, context):
17461749 exp_min = len (self ._int ) + self ._exp - context .prec
17471750 if exp_min > Etop :
17481751 # overflow: exp_min > Etop iff self.adjusted() > Emax
1749- ans = context ._raise_error (Overflow , 'above Emax' , self ._sign )
1750- context ._raise_error (Inexact )
1751- context ._raise_error (Rounded )
1752+ with context ._defer_traps ():
1753+ ans = context ._raise_error (Overflow , 'above Emax' , self ._sign )
1754+ context ._raise_error (Inexact )
1755+ context ._raise_error (Rounded )
17521756 return ans
17531757
17541758 self_is_subnormal = exp_min < Etiny
@@ -1770,34 +1774,38 @@ def _fix(self, context):
17701774 coeff = coeff [:- 1 ]
17711775 exp_min += 1
17721776
1773- # check whether the rounding pushed the exponent out of range
1774- if exp_min > Etop :
1775- ans = context ._raise_error (Overflow , 'above Emax' , self ._sign )
1776- else :
1777- ans = _dec_from_triple (self ._sign , coeff , exp_min )
1777+ with context ._defer_traps ():
1778+ # check whether the rounding pushed the exponent out
1779+ # of range
1780+ if exp_min > Etop :
1781+ ans = context ._raise_error (Overflow , 'above Emax' ,
1782+ self ._sign )
1783+ else :
1784+ ans = _dec_from_triple (self ._sign , coeff , exp_min )
1785+
1786+ # raise the appropriate signals, taking care to respect
1787+ # the precedence described in the specification
1788+ if changed and self_is_subnormal :
1789+ context ._raise_error (Underflow )
1790+ if self_is_subnormal :
1791+ context ._raise_error (Subnormal )
1792+ if changed :
1793+ context ._raise_error (Inexact )
1794+ context ._raise_error (Rounded )
1795+ if not ans :
1796+ # raise Clamped on underflow to 0
1797+ context ._raise_error (Clamped )
1798+ return ans
17781799
1779- # raise the appropriate signals, taking care to respect
1780- # the precedence described in the specification
1781- if changed and self_is_subnormal :
1782- context ._raise_error (Underflow )
1800+ with context ._defer_traps ():
17831801 if self_is_subnormal :
17841802 context ._raise_error (Subnormal )
1785- if changed :
1786- context ._raise_error (Inexact )
1787- context ._raise_error (Rounded )
1788- if not ans :
1789- # raise Clamped on underflow to 0
1790- context ._raise_error (Clamped )
1791- return ans
1792-
1793- if self_is_subnormal :
1794- context ._raise_error (Subnormal )
17951803
1796- # fold down if clamp == 1 and self has too few digits
1797- if context .clamp == 1 and self ._exp > Etop :
1798- context ._raise_error (Clamped )
1799- self_padded = self ._int + '0' * (self ._exp - Etop )
1800- return _dec_from_triple (self ._sign , self_padded , Etop )
1804+ # fold down if clamp == 1 and self has too few digits
1805+ if context .clamp == 1 and self ._exp > Etop :
1806+ context ._raise_error (Clamped )
1807+ self_padded = self ._int + '0' * (self ._exp - Etop )
1808+ return _dec_from_triple (self ._sign , self_padded , Etop )
18011809
18021810 # here self was representable to begin with; return unchanged
18031811 return Decimal (self )
@@ -2445,8 +2453,9 @@ def __pow__(self, other, modulo=None, context=None):
24452453 exp = 1 - context .prec
24462454 context ._raise_error (Rounded )
24472455 else :
2448- context ._raise_error (Inexact )
2449- context ._raise_error (Rounded )
2456+ with context ._defer_traps ():
2457+ context ._raise_error (Inexact )
2458+ context ._raise_error (Rounded )
24502459 exp = 1 - context .prec
24512460
24522461 return _dec_from_triple (result_sign , '1' + '0' * - exp , exp )
@@ -2554,11 +2563,13 @@ def __pow__(self, other, modulo=None, context=None):
25542563 # Inexact, Rounded, Clamped. Overflow needs the correct
25552564 # arguments. Note that the order of the exceptions is
25562565 # important here.
2557- if newcontext .flags [Overflow ]:
2558- context ._raise_error (Overflow , 'above Emax' , ans ._sign )
2559- for exception in Underflow , Subnormal , Inexact , Rounded , Clamped :
2560- if newcontext .flags [exception ]:
2561- context ._raise_error (exception )
2566+ with context ._defer_traps ():
2567+ if newcontext .flags [Overflow ]:
2568+ context ._raise_error (Overflow , 'above Emax' , ans ._sign )
2569+ for exception in (Underflow , Subnormal , Inexact , Rounded ,
2570+ Clamped ):
2571+ if newcontext .flags [exception ]:
2572+ context ._raise_error (exception )
25622573
25632574 else :
25642575 ans = ans ._fix (context )
@@ -2645,17 +2656,18 @@ def quantize(self, exp, rounding=None, context=None):
26452656 return context ._raise_error (InvalidOperation ,
26462657 'quantize result has too many digits for current context' )
26472658
2648- # raise appropriate flags
2649- if ans and ans .adjusted () < context .Emin :
2650- context ._raise_error (Subnormal )
2651- if ans ._exp > self ._exp :
2652- if ans != self :
2653- context ._raise_error (Inexact )
2654- context ._raise_error (Rounded )
2659+ with context ._defer_traps ():
2660+ # raise appropriate flags
2661+ if ans and ans .adjusted () < context .Emin :
2662+ context ._raise_error (Subnormal )
2663+ if ans ._exp > self ._exp :
2664+ if ans != self :
2665+ context ._raise_error (Inexact )
2666+ context ._raise_error (Rounded )
26552667
2656- # call to fix takes care of any necessary folddown, and
2657- # signals Clamped if necessary
2658- ans = ans ._fix (context )
2668+ # call to fix takes care of any necessary folddown, and
2669+ # signals Clamped if necessary
2670+ ans = ans ._fix (context )
26592671 return ans
26602672
26612673 def same_quantum (self , other , context = None ):
@@ -2754,9 +2766,10 @@ def to_integral_exact(self, rounding=None, context=None):
27542766 if rounding is None :
27552767 rounding = context .rounding
27562768 ans = self ._rescale (0 , rounding )
2757- if ans != self :
2758- context ._raise_error (Inexact )
2759- context ._raise_error (Rounded )
2769+ with context ._defer_traps ():
2770+ if ans != self :
2771+ context ._raise_error (Inexact )
2772+ context ._raise_error (Rounded )
27602773 return ans
27612774
27622775 def to_integral_value (self , rounding = None , context = None ):
@@ -3646,21 +3659,22 @@ def next_toward(self, other, context=None):
36463659 ans = self .next_minus (context )
36473660
36483661 # decide which flags to raise using value of ans
3649- if ans ._isinfinity ():
3650- context ._raise_error (Overflow ,
3651- 'Infinite result from next_toward' ,
3652- ans ._sign )
3653- context ._raise_error (Inexact )
3654- context ._raise_error (Rounded )
3655- elif ans .adjusted () < context .Emin :
3656- context ._raise_error (Underflow )
3657- context ._raise_error (Subnormal )
3658- context ._raise_error (Inexact )
3659- context ._raise_error (Rounded )
3660- # if precision == 1 then we don't raise Clamped for a
3661- # result 0E-Etiny.
3662- if not ans :
3663- context ._raise_error (Clamped )
3662+ with context ._defer_traps ():
3663+ if ans ._isinfinity ():
3664+ context ._raise_error (Overflow ,
3665+ 'Infinite result from next_toward' ,
3666+ ans ._sign )
3667+ context ._raise_error (Inexact )
3668+ context ._raise_error (Rounded )
3669+ elif ans .adjusted () < context .Emin :
3670+ context ._raise_error (Underflow )
3671+ context ._raise_error (Subnormal )
3672+ context ._raise_error (Inexact )
3673+ context ._raise_error (Rounded )
3674+ # if precision == 1 then we don't raise Clamped for a
3675+ # result 0E-Etiny.
3676+ if not ans :
3677+ context ._raise_error (Clamped )
36643678
36653679 return ans
36663680
@@ -3988,6 +4002,12 @@ def __init__(self, prec=None, rounding=None, Emin=None, Emax=None,
39884002 else :
39894003 self ._ignored_flags = _ignored_flags
39904004
4005+ # State for _defer_traps: the nesting level of deferral blocks
4006+ # and the first trapped exception waiting to be raised by the
4007+ # outermost block. Transient: not copied or pickled.
4008+ self ._deferred_traps = 0
4009+ self ._deferred_error = None
4010+
39914011 if traps is None :
39924012 self .traps = dc .traps .copy ()
39934013 elif not isinstance (traps , dict ):
@@ -4046,7 +4066,7 @@ def __setattr__(self, name, value):
40464066 return object .__setattr__ (self , name , value )
40474067 elif name == 'flags' or name == 'traps' :
40484068 return self ._set_signal_dict (name , value )
4049- elif name == '_ignored_flags' :
4069+ elif name in ( '_ignored_flags' , '_deferred_traps' , '_deferred_error' ) :
40504070 return object .__setattr__ (self , name , value )
40514071 else :
40524072 raise AttributeError (
@@ -4134,10 +4154,51 @@ def _raise_error(self, condition, explanation = None, *args):
41344154 # The errors define how to handle themselves.
41354155 return condition ().handle (self , * args )
41364156
4157+ if self ._deferred_traps :
4158+ # A _defer_traps block is recording the signals of a
4159+ # multi-signal operation: remember the first trapped
4160+ # exception instead of raising it now, so that the
4161+ # remaining signals can still set their flags. The block
4162+ # raises it on exit. Return the default response, exactly
4163+ # as if the signal had not been trapped.
4164+ if self ._deferred_error is None :
4165+ self ._deferred_error = error (explanation )
4166+ return condition ().handle (self , * args )
4167+
41374168 # Errors should only be risked on copies of the context
41384169 # self._ignored_flags = []
41394170 raise error (explanation )
41404171
4172+ @_contextmanager
4173+ def _defer_traps (self ):
4174+ """Defer trapped signals until the end of the with block.
4175+
4176+ Some operations signal several conditions at once (for
4177+ example, Overflow together with Inexact and Rounded). The
4178+ specification requires the flag of every condition encountered
4179+ to be set, and the C implementation records the full set of
4180+ flags before raising a trapped exception. Within this block,
4181+ _raise_error does the same: it sets the flag of each signal
4182+ and returns its default response, and the first trapped
4183+ exception (in the order the signals are raised, which is the
4184+ precedence order of the specification) is raised when the
4185+ outermost block exits. If the block raises another exception,
4186+ any deferred exception is discarded.
4187+ """
4188+ self ._deferred_traps += 1
4189+ try :
4190+ yield
4191+ except BaseException :
4192+ if self ._deferred_traps == 1 :
4193+ self ._deferred_error = None
4194+ raise
4195+ finally :
4196+ self ._deferred_traps -= 1
4197+ if self ._deferred_traps == 0 and self ._deferred_error is not None :
4198+ error = self ._deferred_error
4199+ self ._deferred_error = None
4200+ raise error
4201+
41414202 def _ignore_all_flags (self ):
41424203 """Ignore all flags, if they are raised"""
41434204 return self ._ignore_flags (* _signals )
0 commit comments