[pypy-svn] pypy default: (Kevin Burke, a bit of arigo)
arigo
commits-noreply at bitbucket.org
Mon Feb 14 17:29:04 CET 2011
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r41911:665baf750859
Date: 2011-02-14 17:27 +0100
http://bitbucket.org/pypy/pypy/changeset/665baf750859/
Log: (Kevin Burke, a bit of arigo)
Implement complex.__format__().
diff --git a/pypy/objspace/std/complexobject.py b/pypy/objspace/std/complexobject.py
--- a/pypy/objspace/std/complexobject.py
+++ b/pypy/objspace/std/complexobject.py
@@ -1,5 +1,6 @@
from pypy.interpreter import gateway
from pypy.interpreter.error import OperationError
+from pypy.objspace.std import newformat
from pypy.objspace.std.model import registerimplementation, W_Object
from pypy.objspace.std.register_all import register_all
from pypy.objspace.std.floatobject import W_FloatObject, _hash_float
@@ -207,11 +208,11 @@
raise OperationError(space.w_OverflowError, space.wrap(str(e)))
def eq__Complex_Complex(space, w_complex1, w_complex2):
- return space.newbool((w_complex1.realval == w_complex2.realval) and
+ return space.newbool((w_complex1.realval == w_complex2.realval) and
(w_complex1.imagval == w_complex2.imagval))
def ne__Complex_Complex(space, w_complex1, w_complex2):
- return space.newbool((w_complex1.realval != w_complex2.realval) or
+ return space.newbool((w_complex1.realval != w_complex2.realval) or
(w_complex1.imagval != w_complex2.imagval))
def eq__Complex_Long(space, w_complex1, w_long2):
@@ -288,5 +289,8 @@
return space.wrap('(' + str_format(w_complex.realval)
+ sign + str_format(w_complex.imagval) + 'j)')
+def format__Complex_ANY(space, w_complex, w_format_spec):
+ return newformat.run_formatter(space, w_format_spec, "format_complex", w_complex)
+
from pypy.objspace.std import complextype
register_all(vars(), complextype)
diff --git a/pypy/objspace/std/test/test_complexobject.py b/pypy/objspace/std/test/test_complexobject.py
--- a/pypy/objspace/std/test/test_complexobject.py
+++ b/pypy/objspace/std/test/test_complexobject.py
@@ -153,7 +153,7 @@
raises(ZeroDivisionError, complex.__div__, 1+1j, 0+0j)
# FIXME: The following currently crashes on Alpha
- # raises(OverflowError, pow, 1e200+1j, 1e200+1j)
+ raises(OverflowError, pow, 1e200+1j, 1e200+1j)
def test_truediv(self):
assert self.almost_equal(complex.__truediv__(2+0j, 1+1j), 1-1j)
@@ -460,7 +460,6 @@
assert complex(a) == 42j
def test_format(self):
- skip("FIXME")
# empty format string is same as str()
assert format(1+3j, '') == str(1+3j)
assert format(1.5+3.5j, '') == str(1.5+3.5j)
@@ -471,9 +470,11 @@
# empty presentation type should still be analogous to str,
# even when format string is nonempty (issue #5920).
+
+ assert format(3.2, '-') == str(3.2)
assert format(3.2+0j, '-') == str(3.2+0j)
assert format(3.2+0j, '<') == str(3.2+0j)
- z = 4/7. - 100j/7.
+ z = 10/7. - 100j/7.
assert format(z, '') == str(z)
assert format(z, '-') == str(z)
assert format(z, '<') == str(z)
@@ -537,12 +538,16 @@
raises(ValueError, (1.5+3j).__format__, '=20')
# integer presentation types are an error
- for t in 'bcdoxX':
+ for t in 'bcdoxX%':
raises(ValueError, (1.5+0.5j).__format__, t)
# make sure everything works in ''.format()
assert '*{0:.3f}*'.format(3.14159+2.71828j) == '*3.142+2.718j*'
+ assert u'*{0:.3f}*'.format(3.14159+2.71828j) == u'*3.142+2.718j*'
+ assert u'{:-}'.format(1.5+3.5j) == u'(1.5+3.5j)'
+ INF = float("inf")
+ NAN = float("nan")
# issue 3382: 'f' and 'F' with inf's and nan's
assert '{0:f}'.format(INF+0j) == 'inf+0.000000j'
assert '{0:F}'.format(INF+0j) == 'INF+0.000000j'
diff --git a/pypy/objspace/std/newformat.py b/pypy/objspace/std/newformat.py
--- a/pypy/objspace/std/newformat.py
+++ b/pypy/objspace/std/newformat.py
@@ -5,6 +5,7 @@
from pypy.interpreter.error import OperationError
from pypy.rlib import rstring, runicode, rlocale, rarithmetic
from pypy.rlib.objectmodel import specialize
+from pypy.rlib.rarithmetic import copysign, formatd
@specialize.argtype(1)
@@ -366,6 +367,9 @@
def format_float(self, w_num):
raise NotImplementedError
+ def format_complex(self, w_num):
+ raise NotImplementedError
+
INT_KIND = 1
LONG_KIND = 2
@@ -380,6 +384,7 @@
"""__format__ implementation for builtin types."""
_annspecialcase_ = "specialize:ctr_location"
+ _grouped_digits = None
def __init__(self, space, is_unicode, spec):
self.space = space
@@ -478,6 +483,7 @@
return False
def _calc_padding(self, string, length):
+ """compute left and right padding, return total width of string"""
if self._width != -1 and length < self._width:
total = self._width
else:
@@ -568,6 +574,17 @@
def _calc_num_width(self, n_prefix, sign_char, to_number, n_number,
n_remainder, has_dec, digits):
+ """Calculate widths of all parts of formatted number.
+
+ Output will look like:
+
+ <lpadding> <sign> <prefix> <spadding> <grouped_digits> <decimal>
+ <remainder> <rpadding>
+
+ sign is computed from self._sign, and the sign of the number
+ prefix is given
+ digits is known
+ """
spec = NumberSpec()
spec.n_digits = n_number - n_remainder - has_dec
spec.n_prefix = n_prefix
@@ -577,6 +594,7 @@
spec.n_spadding = 0
spec.n_rpadding = 0
spec.n_min_width = 0
+ spec.n_total = 0
spec.sign = "\0"
spec.n_sign = 0
sign = self._sign
@@ -612,6 +630,9 @@
spec.n_spadding = n_padding
else:
raise AssertionError("shouldn't reach")
+ spec.n_total = spec.n_lpadding + spec.n_sign + spec.n_prefix + \
+ spec.n_spadding + n_grouped_digits + \
+ spec.n_decimal + spec.n_remainder + spec.n_rpadding
return spec
def _fill_digits(self, buf, digits, d_state, n_chars, n_zeros,
@@ -677,7 +698,7 @@
def _fill_number(self, spec, num, to_digits, to_prefix, fill_char,
- to_remainder, upper):
+ to_remainder, upper, grouped_digits=None):
out = self._builder()
if spec.n_lpadding:
out.append_multiple_char(fill_char[0], spec.n_lpadding)
@@ -696,7 +717,8 @@
out.append_multiple_char(fill_char[0], spec.n_spadding)
if spec.n_digits != 0:
if self._loc_thousands:
- digits = self._grouped_digits
+ digits = grouped_digits if grouped_digits is not None \
+ else self._grouped_digits
else:
stop = to_digits + spec.n_digits
assert stop >= 0
@@ -710,7 +732,8 @@
out.append(num[to_remainder:])
if spec.n_rpadding:
out.append_multiple_char(fill_char[0], spec.n_rpadding)
- return self.space.wrap(out.build())
+ #if complex, need to call twice - just retun the buffer
+ return out.build()
def _format_int_or_long(self, w_num, kind):
space = self.space
@@ -768,8 +791,8 @@
n_remainder, False, result)
fill = self._lit(" ") if self._fill_char == "\0" else self._fill_char
upper = self._type == "X"
- return self._fill_number(spec, result, to_numeric, to_prefix,
- fill, to_remainder, upper)
+ return self.space.wrap(self._fill_number(spec, result, to_numeric,
+ to_prefix, fill, to_remainder, upper))
def _long_to_base(self, base, value):
prefix = ""
@@ -855,6 +878,8 @@
self._unknown_presentation("int" if kind == INT_KIND else "long")
def _parse_number(self, s, i):
+ """Determine if s has a decimal point, and the index of the first #
+ after the decimal, or the end of the number."""
length = len(s)
while i < length and "0" <= s[i] <= "9":
i += 1
@@ -862,9 +887,11 @@
dec_point = i < length and s[i] == "."
if dec_point:
rest += 1
+ #differs from CPython method - CPython sets n_remainder
return dec_point, rest
def _format_float(self, w_float):
+ """helper for format_float"""
space = self.space
flags = 0
default_precision = 6
@@ -909,8 +936,8 @@
spec = self._calc_num_width(0, sign, to_number, n_digits,
n_remainder, have_dec_point, digits)
fill = self._lit(" ") if self._fill_char == "\0" else self._fill_char
- return self._fill_number(spec, digits, to_number, 0, fill,
- to_remainder, False)
+ return self.space.wrap(self._fill_number(spec, digits, to_number, 0,
+ fill, to_remainder, False))
def format_float(self, w_float):
space = self.space
@@ -931,6 +958,168 @@
return self._format_float(w_float)
self._unknown_presentation("float")
+ def _format_complex(self, w_complex):
+ space = self.space
+ tp = self._type
+ self._get_locale(tp)
+ default_precision = 6
+ if self._align == "=":
+ # '=' alignment is invalid
+ msg = ("'=' alignment flag is not allowed in"
+ " complex format specifier")
+ raise OperationError(space.w_ValueError, space.wrap(msg))
+ if self._fill_char == "0":
+ #zero padding is invalid
+ msg = "Zero padding is not allowed in complex format specifier"
+ raise OperationError(space.w_ValueError, space.wrap(msg))
+ if self._alternate:
+ #alternate is invalid
+ msg = "Alternate form %s not allowed in complex format specifier"
+ raise OperationError(space.w_ValueError,
+ space.wrap(msg % (self._alternate)))
+ skip_re = 0
+ add_parens = 0
+ if tp == "\0":
+ #should mirror str() output
+ tp = "g"
+ default_precision = 12
+ #test if real part is non-zero
+ if (w_complex.realval == 0 and
+ copysign(1., w_complex.realval) == 1.):
+ skip_re = 1
+ else:
+ add_parens = 1
+
+ if tp == "n":
+ #same as 'g' except for locale, taken care of later
+ tp = "g"
+
+ #check if precision not set
+ if self._precision == -1:
+ self._precision = default_precision
+
+ #might want to switch to double_to_string from formatd
+ #in CPython it's named 're' - clashes with re module
+ re_num = formatd(w_complex.realval, tp, self._precision)
+ im_num = formatd(w_complex.imagval, tp, self._precision)
+ n_re_digits = len(re_num)
+ n_im_digits = len(im_num)
+
+ to_real_number = 0
+ to_imag_number = 0
+ re_sign = im_sign = ''
+ #if a sign character is in the output, remember it and skip
+ if re_num[0] == "-":
+ re_sign = "-"
+ to_real_number = 1
+ n_re_digits -= 1
+ if im_num[0] == "-":
+ im_sign = "-"
+ to_imag_number = 1
+ n_im_digits -= 1
+
+ #turn off padding - do it after number composition
+ #calc_num_width uses self._width, so assign to temporary variable,
+ #calculate width of real and imag parts, then reassign padding, align
+ tmp_fill_char = self._fill_char
+ tmp_align = self._align
+ tmp_width = self._width
+ self._fill_char = "\0"
+ self._align = "<"
+ self._width = -1
+
+ #determine if we have remainder, might include dec or exponent or both
+ re_have_dec, re_remainder_ptr = self._parse_number(re_num,
+ to_real_number)
+ im_have_dec, im_remainder_ptr = self._parse_number(im_num,
+ to_imag_number)
+
+ if self.is_unicode:
+ re_num = re_num.decode("ascii")
+ im_num = im_num.decode("ascii")
+
+ #set remainder, in CPython _parse_number sets this
+ #using n_re_digits causes tests to fail
+ re_n_remainder = len(re_num) - re_remainder_ptr
+ im_n_remainder = len(im_num) - im_remainder_ptr
+ re_spec = self._calc_num_width(0, re_sign, to_real_number, n_re_digits,
+ re_n_remainder, re_have_dec,
+ re_num)
+
+ #capture grouped digits b/c _fill_number reads from self._grouped_digits
+ #self._grouped_digits will get overwritten in imaginary calc_num_width
+ re_grouped_digits = self._grouped_digits
+ if not skip_re:
+ self._sign = "+"
+ im_spec = self._calc_num_width(0, im_sign, to_imag_number, n_im_digits,
+ im_n_remainder, im_have_dec,
+ im_num)
+
+ im_grouped_digits = self._grouped_digits
+ if skip_re:
+ re_spec.n_total = 0
+
+ #reassign width, alignment, fill character
+ self._align = tmp_align
+ self._width = tmp_width
+ self._fill_char = tmp_fill_char
+
+ #compute L and R padding - stored in self._left_pad and self._right_pad
+ self._calc_padding(self.empty, re_spec.n_total + im_spec.n_total + 1 +
+ add_parens * 2)
+
+ out = self._builder()
+ fill = self._fill_char
+ if fill == "\0":
+ fill = self._lit(" ")[0]
+
+ #compose the string
+ #add left padding
+ out.append_multiple_char(fill, self._left_pad)
+ if add_parens:
+ out.append(self._lit('(')[0])
+
+ #if the no. has a real component, add it
+ if not skip_re:
+ out.append(self._fill_number(re_spec, re_num, to_real_number, 0,
+ fill, re_remainder_ptr, False,
+ re_grouped_digits))
+
+ #add imaginary component
+ out.append(self._fill_number(im_spec, im_num, to_imag_number, 0,
+ fill, im_remainder_ptr, False,
+ im_grouped_digits))
+
+ #add 'j' character
+ out.append(self._lit('j')[0])
+
+ if add_parens:
+ out.append(self._lit(')')[0])
+
+ #add right padding
+ out.append_multiple_char(fill, self._right_pad)
+
+ return self.space.wrap(out.build())
+
+
+ def format_complex(self, w_complex):
+ """return the string representation of a complex number"""
+ space = self.space
+ #parse format specification, set associated variables
+ if self._parse_spec("\0", ">"):
+ return space.str(w_complex)
+ tp = self._type
+ if (tp == "\0" or
+ tp == "e" or
+ tp == "E" or
+ tp == "f" or
+ tp == "F" or
+ tp == "g" or
+ tp == "G" or
+ tp == "n"):
+ return self._format_complex(w_complex)
+ self._unknown_presentation("complex")
+
def unicode_formatter(space, spec):
return Formatter(space, True, spec)
More information about the Pypy-commit
mailing list