@@ -2381,6 +2381,29 @@ def _rescale(self, exp, rounding):
23812381 coeff = str (int (coeff )+ 1 )
23822382 return _dec_from_triple (self ._sign , coeff , exp )
23832383
2384+ def _round (self , places , rounding ):
2385+ """Round a nonzero, nonspecial Decimal to a fixed number of
2386+ significant figures, using the given rounding mode.
2387+
2388+ Infinities, NaNs and zeros are returned unaltered.
2389+
2390+ This operation is quiet: it raises no flags, and uses no
2391+ information from the context.
2392+
2393+ """
2394+ if places <= 0 :
2395+ raise ValueError ("argument should be at least 1 in _round" )
2396+ if self ._is_special or not self :
2397+ return Decimal (self )
2398+ ans = self ._rescale (self .adjusted ()+ 1 - places , rounding )
2399+ # it can happen that the rescale alters the adjusted exponent;
2400+ # for example when rounding 99.97 to 3 significant figures.
2401+ # When this happens we end up with an extra 0 at the end of
2402+ # the number; a second rescale fixes this.
2403+ if ans .adjusted () != self .adjusted ():
2404+ ans = ans ._rescale (ans .adjusted ()+ 1 - places , rounding )
2405+ return ans
2406+
23842407 def to_integral_exact (self , rounding = None , context = None ):
23852408 """Rounds to a nearby integer.
23862409
@@ -3432,6 +3455,95 @@ def __deepcopy__(self, memo):
34323455 return self # My components are also immutable
34333456 return self .__class__ (str (self ))
34343457
3458+ # PEP 3101 support. See also _parse_format_specifier and _format_align
3459+ def __format__ (self , specifier , context = None ):
3460+ """Format a Decimal instance according to the given specifier.
3461+
3462+ The specifier should be a standard format specifier, with the
3463+ form described in PEP 3101. Formatting types 'e', 'E', 'f',
3464+ 'F', 'g', 'G', and '%' are supported. If the formatting type
3465+ is omitted it defaults to 'g' or 'G', depending on the value
3466+ of context.capitals.
3467+
3468+ At this time the 'n' format specifier type (which is supposed
3469+ to use the current locale) is not supported.
3470+ """
3471+
3472+ # Note: PEP 3101 says that if the type is not present then
3473+ # there should be at least one digit after the decimal point.
3474+ # We take the liberty of ignoring this requirement for
3475+ # Decimal---it's presumably there to make sure that
3476+ # format(float, '') behaves similarly to str(float).
3477+ if context is None :
3478+ context = getcontext ()
3479+
3480+ spec = _parse_format_specifier (specifier )
3481+
3482+ # special values don't care about the type or precision...
3483+ if self ._is_special :
3484+ return _format_align (str (self ), spec )
3485+
3486+ # a type of None defaults to 'g' or 'G', depending on context
3487+ # if type is '%', adjust exponent of self accordingly
3488+ if spec ['type' ] is None :
3489+ spec ['type' ] = ['g' , 'G' ][context .capitals ]
3490+ elif spec ['type' ] == '%' :
3491+ self = _dec_from_triple (self ._sign , self ._int , self ._exp + 2 )
3492+
3493+ # round if necessary, taking rounding mode from the context
3494+ rounding = context .rounding
3495+ precision = spec ['precision' ]
3496+ if precision is not None :
3497+ if spec ['type' ] in 'eE' :
3498+ self = self ._round (precision + 1 , rounding )
3499+ elif spec ['type' ] in 'gG' :
3500+ if len (self ._int ) > precision :
3501+ self = self ._round (precision , rounding )
3502+ elif spec ['type' ] in 'fF%' :
3503+ self = self ._rescale (- precision , rounding )
3504+ # special case: zeros with a positive exponent can't be
3505+ # represented in fixed point; rescale them to 0e0.
3506+ elif not self and self ._exp > 0 and spec ['type' ] in 'fF%' :
3507+ self = self ._rescale (0 , rounding )
3508+
3509+ # figure out placement of the decimal point
3510+ leftdigits = self ._exp + len (self ._int )
3511+ if spec ['type' ] in 'fF%' :
3512+ dotplace = leftdigits
3513+ elif spec ['type' ] in 'eE' :
3514+ if not self and precision is not None :
3515+ dotplace = 1 - precision
3516+ else :
3517+ dotplace = 1
3518+ elif spec ['type' ] in 'gG' :
3519+ if self ._exp <= 0 and leftdigits > - 6 :
3520+ dotplace = leftdigits
3521+ else :
3522+ dotplace = 1
3523+
3524+ # figure out main part of numeric string...
3525+ if dotplace <= 0 :
3526+ num = '0.' + '0' * (- dotplace ) + self ._int
3527+ elif dotplace >= len (self ._int ):
3528+ # make sure we're not padding a '0' with extra zeros on the right
3529+ assert dotplace == len (self ._int ) or self ._int != '0'
3530+ num = self ._int + '0' * (dotplace - len (self ._int ))
3531+ else :
3532+ num = self ._int [:dotplace ] + '.' + self ._int [dotplace :]
3533+
3534+ # ...then the trailing exponent, or trailing '%'
3535+ if leftdigits != dotplace or spec ['type' ] in 'eE' :
3536+ echar = {'E' : 'E' , 'e' : 'e' , 'G' : 'E' , 'g' : 'e' }[spec ['type' ]]
3537+ num = num + "{0}{1:+}" .format (echar , leftdigits - dotplace )
3538+ elif spec ['type' ] == '%' :
3539+ num = num + '%'
3540+
3541+ # add sign
3542+ if self ._sign == 1 :
3543+ num = '-' + num
3544+ return _format_align (num , spec )
3545+
3546+
34353547def _dec_from_triple (sign , coefficient , exponent , special = False ):
34363548 """Create a decimal instance directly, without any validation,
34373549 normalization (e.g. removal of leading zeros) or argument
@@ -5249,8 +5361,136 @@ def _convert_other(other, raiseit=False):
52495361
52505362_all_zeros = re .compile ('0*$' ).match
52515363_exact_half = re .compile ('50*$' ).match
5364+
5365+ ##### PEP3101 support functions ##############################################
5366+ # The functions parse_format_specifier and format_align have little to do
5367+ # with the Decimal class, and could potentially be reused for other pure
5368+ # Python numeric classes that want to implement __format__
5369+ #
5370+ # A format specifier for Decimal looks like:
5371+ #
5372+ # [[fill]align][sign][0][minimumwidth][.precision][type]
5373+ #
5374+
5375+ _parse_format_specifier_regex = re .compile (r"""\A
5376+ (?:
5377+ (?P<fill>.)?
5378+ (?P<align>[<>=^])
5379+ )?
5380+ (?P<sign>[-+ ])?
5381+ (?P<zeropad>0)?
5382+ (?P<minimumwidth>(?!0)\d+)?
5383+ (?:\.(?P<precision>0|(?!0)\d+))?
5384+ (?P<type>[eEfFgG%])?
5385+ \Z
5386+ """ , re .VERBOSE )
5387+
52525388del re
52535389
5390+ def _parse_format_specifier (format_spec ):
5391+ """Parse and validate a format specifier.
5392+
5393+ Turns a standard numeric format specifier into a dict, with the
5394+ following entries:
5395+
5396+ fill: fill character to pad field to minimum width
5397+ align: alignment type, either '<', '>', '=' or '^'
5398+ sign: either '+', '-' or ' '
5399+ minimumwidth: nonnegative integer giving minimum width
5400+ precision: nonnegative integer giving precision, or None
5401+ type: one of the characters 'eEfFgG%', or None
5402+ unicode: either True or False (always True for Python 3.x)
5403+
5404+ """
5405+ m = _parse_format_specifier_regex .match (format_spec )
5406+ if m is None :
5407+ raise ValueError ("Invalid format specifier: " + format_spec )
5408+
5409+ # get the dictionary
5410+ format_dict = m .groupdict ()
5411+
5412+ # defaults for fill and alignment
5413+ fill = format_dict ['fill' ]
5414+ align = format_dict ['align' ]
5415+ if format_dict .pop ('zeropad' ) is not None :
5416+ # in the face of conflict, refuse the temptation to guess
5417+ if fill is not None and fill != '0' :
5418+ raise ValueError ("Fill character conflicts with '0'"
5419+ " in format specifier: " + format_spec )
5420+ if align is not None and align != '=' :
5421+ raise ValueError ("Alignment conflicts with '0' in "
5422+ "format specifier: " + format_spec )
5423+ fill = '0'
5424+ align = '='
5425+ format_dict ['fill' ] = fill or ' '
5426+ format_dict ['align' ] = align or '<'
5427+
5428+ if format_dict ['sign' ] is None :
5429+ format_dict ['sign' ] = '-'
5430+
5431+ # turn minimumwidth and precision entries into integers.
5432+ # minimumwidth defaults to 0; precision remains None if not given
5433+ format_dict ['minimumwidth' ] = int (format_dict ['minimumwidth' ] or '0' )
5434+ if format_dict ['precision' ] is not None :
5435+ format_dict ['precision' ] = int (format_dict ['precision' ])
5436+
5437+ # if format type is 'g' or 'G' then a precision of 0 makes little
5438+ # sense; convert it to 1. Same if format type is unspecified.
5439+ if format_dict ['precision' ] == 0 :
5440+ if format_dict ['type' ] in 'gG' or format_dict ['type' ] is None :
5441+ format_dict ['precision' ] = 1
5442+
5443+ # record whether return type should be str or unicode
5444+ format_dict ['unicode' ] = isinstance (format_spec , unicode )
5445+
5446+ return format_dict
5447+
5448+ def _format_align (body , spec_dict ):
5449+ """Given an unpadded, non-aligned numeric string, add padding and
5450+ aligment to conform with the given format specifier dictionary (as
5451+ output from parse_format_specifier).
5452+
5453+ It's assumed that if body is negative then it starts with '-'.
5454+ Any leading sign ('-' or '+') is stripped from the body before
5455+ applying the alignment and padding rules, and replaced in the
5456+ appropriate position.
5457+
5458+ """
5459+ # figure out the sign; we only examine the first character, so if
5460+ # body has leading whitespace the results may be surprising.
5461+ if len (body ) > 0 and body [0 ] in '-+' :
5462+ sign = body [0 ]
5463+ body = body [1 :]
5464+ else :
5465+ sign = ''
5466+
5467+ if sign != '-' :
5468+ if spec_dict ['sign' ] in ' +' :
5469+ sign = spec_dict ['sign' ]
5470+ else :
5471+ sign = ''
5472+
5473+ # how much extra space do we have to play with?
5474+ minimumwidth = spec_dict ['minimumwidth' ]
5475+ fill = spec_dict ['fill' ]
5476+ padding = fill * (max (minimumwidth - (len (sign + body )), 0 ))
5477+
5478+ align = spec_dict ['align' ]
5479+ if align == '<' :
5480+ result = padding + sign + body
5481+ elif align == '>' :
5482+ result = sign + body + padding
5483+ elif align == '=' :
5484+ result = sign + padding + body
5485+ else : #align == '^'
5486+ half = len (padding )// 2
5487+ result = padding [:half ] + sign + body + padding [half :]
5488+
5489+ # make sure that result is unicode if necessary
5490+ if spec_dict ['unicode' ]:
5491+ result = unicode (result )
5492+
5493+ return result
52545494
52555495##### Useful Constants (internal use only) ################################
52565496
0 commit comments