[Jython-checkins] jython: Add stringlib.IntegerFormatter and use to refactor %-formatting.
jeff.allen
jython-checkins at python.org
Sun Jun 8 14:12:58 CEST 2014
http://hg.python.org/jython/rev/ccf7e598786a
changeset: 7279:ccf7e598786a
parent: 7259:60838278e668
user: Jeff Allen <ja.py at farowl.co.uk>
date: Sun May 25 20:37:09 2014 +0100
summary:
Add stringlib.IntegerFormatter and use to refactor %-formatting.
Updates Lib/test_format from CPython 2.7, but we still need a Jython
version. Big weight-loss in PyString.StringFormatter, and numeric bin(), oct()
and hex() are based on the new code. (Still using Santoso's speeded-up
conversion methods.)
files:
Lib/test/test_format.py | 473 +++--
src/org/python/core/PyInteger.java | 29 +-
src/org/python/core/PyLong.java | 34 +-
src/org/python/core/PyString.java | 524 ++----
src/org/python/core/__builtin__.java | 20 +-
src/org/python/core/stringlib/FloatFormatter.java | 14 +-
src/org/python/core/stringlib/IntegerFormatter.java | 696 ++++++++++
src/org/python/core/stringlib/InternalFormat.java | 42 +-
8 files changed, 1231 insertions(+), 601 deletions(-)
diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py
--- a/Lib/test/test_format.py
+++ b/Lib/test/test_format.py
@@ -1,14 +1,17 @@
-from test.test_support import verbose, have_unicode, TestFailed, is_jython
import sys
+from test.test_support import verbose, have_unicode, TestFailed
+from test.test_support import is_jython
+import test.test_support as test_support
+import unittest
+
+maxsize = test_support.MAX_Py_ssize_t
# test string formatting operator (I am not sure if this is being tested
# elsewhere but, surely, some of the given cases are *not* tested because
# they crash python)
# test on unicode strings as well
-overflowok = 1
-
-def testformat(formatstr, args, output=None):
+def testformat(formatstr, args, output=None, limit=None, overflowok=False):
if verbose:
if output:
print "%s %% %s =? %s ..." %\
@@ -23,231 +26,289 @@
if verbose:
print 'overflow (this is fine)'
else:
- if output and result != output:
+ if output and limit is None and result != output:
if verbose:
print 'no'
- print "%s %% %s == %s != %s" %\
- (repr(formatstr), repr(args), repr(result), repr(output))
+ raise AssertionError("%r %% %r == %r != %r" %
+ (formatstr, args, result, output))
+ # when 'limit' is specified, it determines how many characters
+ # must match exactly; lengths must always match.
+ # ex: limit=5, '12345678' matches '12345___'
+ # (mainly for floating point format tests for which an exact match
+ # can't be guaranteed due to rounding and representation errors)
+ elif output and limit is not None and (
+ len(result)!=len(output) or result[:limit]!=output[:limit]):
+ if verbose:
+ print 'no'
+ print "%s %% %s == %s != %s" % \
+ (repr(formatstr), repr(args), repr(result), repr(output))
else:
if verbose:
print 'yes'
-def testboth(formatstr, *args):
- testformat(formatstr, *args)
+
+def testboth(formatstr, *args, **kwargs):
+ testformat(formatstr, *args, **kwargs)
if have_unicode:
- testformat(unicode(formatstr), *args)
+ testformat(unicode(formatstr), *args, **kwargs)
-testboth("%.1d", (1,), "1")
-testboth("%.*d", (sys.maxint,1)) # expect overflow
-testboth("%.100d", (1,), '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001')
-testboth("%#.117x", (1,), '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001')
-testboth("%#.118x", (1,), '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001')
+class FormatTest(unittest.TestCase):
+ def test_format(self):
+ testboth("%.1d", (1,), "1")
+ testboth("%.*d", (sys.maxint,1), overflowok=True) # expect overflow
+ testboth("%.100d", (1,), '00000000000000000000000000000000000000'
+ '000000000000000000000000000000000000000000000000000000'
+ '00000001', overflowok=True)
+ testboth("%#.117x", (1,), '0x00000000000000000000000000000000000'
+ '000000000000000000000000000000000000000000000000000000'
+ '0000000000000000000000000001',
+ overflowok=True)
+ testboth("%#.118x", (1,), '0x00000000000000000000000000000000000'
+ '000000000000000000000000000000000000000000000000000000'
+ '00000000000000000000000000001',
+ overflowok=True)
-testboth("%f", (1.0,), "1.000000")
-# these are trying to test the limits of the internal magic-number-length
-# formatting buffer, if that number changes then these tests are less
-# effective
-testboth("%#.*g", (109, -1.e+49/3.))
-testboth("%#.*g", (110, -1.e+49/3.))
-testboth("%#.*g", (110, -1.e+100/3.))
+ testboth("%f", (1.0,), "1.000000")
+ # these are trying to test the limits of the internal magic-number-length
+ # formatting buffer, if that number changes then these tests are less
+ # effective
+ testboth("%#.*g", (109, -1.e+49/3.))
+ testboth("%#.*g", (110, -1.e+49/3.))
+ testboth("%#.*g", (110, -1.e+100/3.))
-# test some ridiculously large precision, expect overflow
-testboth('%12.*f', (123456, 1.0))
+ # test some ridiculously large precision, expect overflow
+ # ... Jython remains consistent with the original comment.
+ testboth('%12.*f', (123456, 1.0), overflowok=is_jython)
-# Formatting of long integers. Overflow is not ok
-overflowok = 0
-testboth("%x", 10L, "a")
-testboth("%x", 100000000000L, "174876e800")
-testboth("%o", 10L, "12")
-testboth("%o", 100000000000L, "1351035564000")
-testboth("%d", 10L, "10")
-testboth("%d", 100000000000L, "100000000000")
+ # check for internal overflow validation on length of precision
+ # these tests should no longer cause overflow in Python
+ # 2.7/3.1 and later.
+ testboth("%#.*g", (110, -1.e+100/3.))
+ testboth("%#.*G", (110, -1.e+100/3.))
+ testboth("%#.*f", (110, -1.e+100/3.))
+ testboth("%#.*F", (110, -1.e+100/3.))
-big = 123456789012345678901234567890L
-testboth("%d", big, "123456789012345678901234567890")
-testboth("%d", -big, "-123456789012345678901234567890")
-testboth("%5d", -big, "-123456789012345678901234567890")
-testboth("%31d", -big, "-123456789012345678901234567890")
-testboth("%32d", -big, " -123456789012345678901234567890")
-testboth("%-32d", -big, "-123456789012345678901234567890 ")
-testboth("%032d", -big, "-0123456789012345678901234567890")
-testboth("%-032d", -big, "-123456789012345678901234567890 ")
-testboth("%034d", -big, "-000123456789012345678901234567890")
-testboth("%034d", big, "0000123456789012345678901234567890")
-testboth("%0+34d", big, "+000123456789012345678901234567890")
-testboth("%+34d", big, " +123456789012345678901234567890")
-testboth("%34d", big, " 123456789012345678901234567890")
-testboth("%.2d", big, "123456789012345678901234567890")
-testboth("%.30d", big, "123456789012345678901234567890")
-testboth("%.31d", big, "0123456789012345678901234567890")
-testboth("%32.31d", big, " 0123456789012345678901234567890")
+ # Formatting of long integers. Overflow is not ok
+ testboth("%x", 10L, "a")
+ testboth("%x", 100000000000L, "174876e800")
+ testboth("%o", 10L, "12")
+ testboth("%o", 100000000000L, "1351035564000")
+ testboth("%d", 10L, "10")
+ testboth("%d", 100000000000L, "100000000000")
-big = 0x1234567890abcdef12345L # 21 hex digits
-testboth("%x", big, "1234567890abcdef12345")
-testboth("%x", -big, "-1234567890abcdef12345")
-testboth("%5x", -big, "-1234567890abcdef12345")
-testboth("%22x", -big, "-1234567890abcdef12345")
-testboth("%23x", -big, " -1234567890abcdef12345")
-testboth("%-23x", -big, "-1234567890abcdef12345 ")
-testboth("%023x", -big, "-01234567890abcdef12345")
-testboth("%-023x", -big, "-1234567890abcdef12345 ")
-testboth("%025x", -big, "-0001234567890abcdef12345")
-testboth("%025x", big, "00001234567890abcdef12345")
-testboth("%0+25x", big, "+0001234567890abcdef12345")
-testboth("%+25x", big, " +1234567890abcdef12345")
-testboth("%25x", big, " 1234567890abcdef12345")
-testboth("%.2x", big, "1234567890abcdef12345")
-testboth("%.21x", big, "1234567890abcdef12345")
-testboth("%.22x", big, "01234567890abcdef12345")
-testboth("%23.22x", big, " 01234567890abcdef12345")
-testboth("%-23.22x", big, "01234567890abcdef12345 ")
-testboth("%X", big, "1234567890ABCDEF12345")
-testboth("%#X", big, "0X1234567890ABCDEF12345")
-testboth("%#x", big, "0x1234567890abcdef12345")
-testboth("%#x", -big, "-0x1234567890abcdef12345")
-testboth("%#.23x", -big, "-0x001234567890abcdef12345")
-testboth("%#+.23x", big, "+0x001234567890abcdef12345")
-testboth("%# .23x", big, " 0x001234567890abcdef12345")
-testboth("%#+.23X", big, "+0X001234567890ABCDEF12345")
-testboth("%#-+.23X", big, "+0X001234567890ABCDEF12345")
-testboth("%#-+26.23X", big, "+0X001234567890ABCDEF12345")
-testboth("%#-+27.23X", big, "+0X001234567890ABCDEF12345 ")
-testboth("%#+27.23X", big, " +0X001234567890ABCDEF12345")
-# next one gets two leading zeroes from precision, and another from the
-# 0 flag and the width
-testboth("%#+027.23X", big, "+0X0001234567890ABCDEF12345")
-# same, except no 0 flag
-testboth("%#+27.23X", big, " +0X001234567890ABCDEF12345")
+ big = 123456789012345678901234567890L
+ testboth("%d", big, "123456789012345678901234567890")
+ testboth("%d", -big, "-123456789012345678901234567890")
+ testboth("%5d", -big, "-123456789012345678901234567890")
+ testboth("%31d", -big, "-123456789012345678901234567890")
+ testboth("%32d", -big, " -123456789012345678901234567890")
+ testboth("%-32d", -big, "-123456789012345678901234567890 ")
+ testboth("%032d", -big, "-0123456789012345678901234567890")
+ testboth("%-032d", -big, "-123456789012345678901234567890 ")
+ testboth("%034d", -big, "-000123456789012345678901234567890")
+ testboth("%034d", big, "0000123456789012345678901234567890")
+ testboth("%0+34d", big, "+000123456789012345678901234567890")
+ testboth("%+34d", big, " +123456789012345678901234567890")
+ testboth("%34d", big, " 123456789012345678901234567890")
+ testboth("%.2d", big, "123456789012345678901234567890")
+ testboth("%.30d", big, "123456789012345678901234567890")
+ testboth("%.31d", big, "0123456789012345678901234567890")
+ testboth("%32.31d", big, " 0123456789012345678901234567890")
+ testboth("%d", float(big), "123456________________________", 6)
-big = 012345670123456701234567012345670L # 32 octal digits
-testboth("%o", big, "12345670123456701234567012345670")
-testboth("%o", -big, "-12345670123456701234567012345670")
-testboth("%5o", -big, "-12345670123456701234567012345670")
-testboth("%33o", -big, "-12345670123456701234567012345670")
-testboth("%34o", -big, " -12345670123456701234567012345670")
-testboth("%-34o", -big, "-12345670123456701234567012345670 ")
-testboth("%034o", -big, "-012345670123456701234567012345670")
-testboth("%-034o", -big, "-12345670123456701234567012345670 ")
-testboth("%036o", -big, "-00012345670123456701234567012345670")
-testboth("%036o", big, "000012345670123456701234567012345670")
-testboth("%0+36o", big, "+00012345670123456701234567012345670")
-testboth("%+36o", big, " +12345670123456701234567012345670")
-testboth("%36o", big, " 12345670123456701234567012345670")
-testboth("%.2o", big, "12345670123456701234567012345670")
-testboth("%.32o", big, "12345670123456701234567012345670")
-testboth("%.33o", big, "012345670123456701234567012345670")
-testboth("%34.33o", big, " 012345670123456701234567012345670")
-testboth("%-34.33o", big, "012345670123456701234567012345670 ")
-testboth("%o", big, "12345670123456701234567012345670")
-testboth("%#o", big, "012345670123456701234567012345670")
-testboth("%#o", -big, "-012345670123456701234567012345670")
-testboth("%#.34o", -big, "-0012345670123456701234567012345670")
-testboth("%#+.34o", big, "+0012345670123456701234567012345670")
-testboth("%# .34o", big, " 0012345670123456701234567012345670")
-testboth("%#+.34o", big, "+0012345670123456701234567012345670")
-testboth("%#-+.34o", big, "+0012345670123456701234567012345670")
-testboth("%#-+37.34o", big, "+0012345670123456701234567012345670 ")
-testboth("%#+37.34o", big, " +0012345670123456701234567012345670")
-# next one gets one leading zero from precision
-testboth("%.33o", big, "012345670123456701234567012345670")
-# base marker shouldn't change that, since "0" is redundant
-testboth("%#.33o", big, "012345670123456701234567012345670")
-# but reduce precision, and base marker should add a zero
-testboth("%#.32o", big, "012345670123456701234567012345670")
-# one leading zero from precision, and another from "0" flag & width
-testboth("%034.33o", big, "0012345670123456701234567012345670")
-# base marker shouldn't change that
-testboth("%0#34.33o", big, "0012345670123456701234567012345670")
+ big = 0x1234567890abcdef12345L # 21 hex digits
+ testboth("%x", big, "1234567890abcdef12345")
+ testboth("%x", -big, "-1234567890abcdef12345")
+ testboth("%5x", -big, "-1234567890abcdef12345")
+ testboth("%22x", -big, "-1234567890abcdef12345")
+ testboth("%23x", -big, " -1234567890abcdef12345")
+ testboth("%-23x", -big, "-1234567890abcdef12345 ")
+ testboth("%023x", -big, "-01234567890abcdef12345")
+ testboth("%-023x", -big, "-1234567890abcdef12345 ")
+ testboth("%025x", -big, "-0001234567890abcdef12345")
+ testboth("%025x", big, "00001234567890abcdef12345")
+ testboth("%0+25x", big, "+0001234567890abcdef12345")
+ testboth("%+25x", big, " +1234567890abcdef12345")
+ testboth("%25x", big, " 1234567890abcdef12345")
+ testboth("%.2x", big, "1234567890abcdef12345")
+ testboth("%.21x", big, "1234567890abcdef12345")
+ testboth("%.22x", big, "01234567890abcdef12345")
+ testboth("%23.22x", big, " 01234567890abcdef12345")
+ testboth("%-23.22x", big, "01234567890abcdef12345 ")
+ testboth("%X", big, "1234567890ABCDEF12345")
+ testboth("%#X", big, "0X1234567890ABCDEF12345")
+ testboth("%#x", big, "0x1234567890abcdef12345")
+ testboth("%#x", -big, "-0x1234567890abcdef12345")
+ testboth("%#.23x", -big, "-0x001234567890abcdef12345")
+ testboth("%#+.23x", big, "+0x001234567890abcdef12345")
+ testboth("%# .23x", big, " 0x001234567890abcdef12345")
+ testboth("%#+.23X", big, "+0X001234567890ABCDEF12345")
+ testboth("%#-+.23X", big, "+0X001234567890ABCDEF12345")
+ testboth("%#-+26.23X", big, "+0X001234567890ABCDEF12345")
+ testboth("%#-+27.23X", big, "+0X001234567890ABCDEF12345 ")
+ testboth("%#+27.23X", big, " +0X001234567890ABCDEF12345")
+ # next one gets two leading zeroes from precision, and another from the
+ # 0 flag and the width
+ testboth("%#+027.23X", big, "+0X0001234567890ABCDEF12345")
+ # same, except no 0 flag
+ testboth("%#+27.23X", big, " +0X001234567890ABCDEF12345")
+ testboth("%x", float(big), "123456_______________", 6)
-# Some small ints, in both Python int and long flavors).
-testboth("%d", 42, "42")
-testboth("%d", -42, "-42")
-testboth("%d", 42L, "42")
-testboth("%d", -42L, "-42")
-testboth("%#x", 1, "0x1")
-testboth("%#x", 1L, "0x1")
-testboth("%#X", 1, "0X1")
-testboth("%#X", 1L, "0X1")
-testboth("%#o", 1, "01")
-testboth("%#o", 1L, "01")
-testboth("%#o", 0, "0")
-testboth("%#o", 0L, "0")
-testboth("%o", 0, "0")
-testboth("%o", 0L, "0")
-testboth("%d", 0, "0")
-testboth("%d", 0L, "0")
-testboth("%#x", 0, "0x0")
-testboth("%#x", 0L, "0x0")
-testboth("%#X", 0, "0X0")
-testboth("%#X", 0L, "0X0")
+ big = 012345670123456701234567012345670L # 32 octal digits
+ testboth("%o", big, "12345670123456701234567012345670")
+ testboth("%o", -big, "-12345670123456701234567012345670")
+ testboth("%5o", -big, "-12345670123456701234567012345670")
+ testboth("%33o", -big, "-12345670123456701234567012345670")
+ testboth("%34o", -big, " -12345670123456701234567012345670")
+ testboth("%-34o", -big, "-12345670123456701234567012345670 ")
+ testboth("%034o", -big, "-012345670123456701234567012345670")
+ testboth("%-034o", -big, "-12345670123456701234567012345670 ")
+ testboth("%036o", -big, "-00012345670123456701234567012345670")
+ testboth("%036o", big, "000012345670123456701234567012345670")
+ testboth("%0+36o", big, "+00012345670123456701234567012345670")
+ testboth("%+36o", big, " +12345670123456701234567012345670")
+ testboth("%36o", big, " 12345670123456701234567012345670")
+ testboth("%.2o", big, "12345670123456701234567012345670")
+ testboth("%.32o", big, "12345670123456701234567012345670")
+ testboth("%.33o", big, "012345670123456701234567012345670")
+ testboth("%34.33o", big, " 012345670123456701234567012345670")
+ testboth("%-34.33o", big, "012345670123456701234567012345670 ")
+ testboth("%o", big, "12345670123456701234567012345670")
+ testboth("%#o", big, "012345670123456701234567012345670")
+ testboth("%#o", -big, "-012345670123456701234567012345670")
+ testboth("%#.34o", -big, "-0012345670123456701234567012345670")
+ testboth("%#+.34o", big, "+0012345670123456701234567012345670")
+ testboth("%# .34o", big, " 0012345670123456701234567012345670")
+ testboth("%#+.34o", big, "+0012345670123456701234567012345670")
+ testboth("%#-+.34o", big, "+0012345670123456701234567012345670")
+ testboth("%#-+37.34o", big, "+0012345670123456701234567012345670 ")
+ testboth("%#+37.34o", big, " +0012345670123456701234567012345670")
+ # next one gets one leading zero from precision
+ testboth("%.33o", big, "012345670123456701234567012345670")
+ # base marker shouldn't change that, since "0" is redundant
+ testboth("%#.33o", big, "012345670123456701234567012345670")
+ # but reduce precision, and base marker should add a zero
+ testboth("%#.32o", big, "012345670123456701234567012345670")
+ # one leading zero from precision, and another from "0" flag & width
+ testboth("%034.33o", big, "0012345670123456701234567012345670")
+ # base marker shouldn't change that
+ testboth("%0#34.33o", big, "0012345670123456701234567012345670")
+ testboth("%o", float(big), "123456__________________________", 6)
-testboth("%x", 0x42, "42")
-testboth("%x", -0x42, "-42")
-testboth("%x", 0x42L, "42")
-testboth("%x", -0x42L, "-42")
+ # Some small ints, in both Python int and long flavors).
+ testboth("%d", 42, "42")
+ testboth("%d", -42, "-42")
+ testboth("%d", 42L, "42")
+ testboth("%d", -42L, "-42")
+ testboth("%d", 42.0, "42")
+ testboth("%#x", 1, "0x1")
+ testboth("%#x", 1L, "0x1")
+ testboth("%#X", 1, "0X1")
+ testboth("%#X", 1L, "0X1")
+ testboth("%#x", 1.0, "0x1")
+ testboth("%#o", 1, "01")
+ testboth("%#o", 1L, "01")
+ testboth("%#o", 0, "0")
+ testboth("%#o", 0L, "0")
+ testboth("%o", 0, "0")
+ testboth("%o", 0L, "0")
+ testboth("%d", 0, "0")
+ testboth("%d", 0L, "0")
+ testboth("%#x", 0, "0x0")
+ testboth("%#x", 0L, "0x0")
+ testboth("%#X", 0, "0X0")
+ testboth("%#X", 0L, "0X0")
-testboth("%o", 042, "42")
-testboth("%o", -042, "-42")
-testboth("%o", 042L, "42")
-testboth("%o", -042L, "-42")
+ testboth("%x", 0x42, "42")
+ testboth("%x", -0x42, "-42")
+ testboth("%x", 0x42L, "42")
+ testboth("%x", -0x42L, "-42")
+ testboth("%x", float(0x42), "42")
-# Test exception for unknown format characters
-if verbose:
- print 'Testing exceptions'
+ testboth("%o", 042, "42")
+ testboth("%o", -042, "-42")
+ testboth("%o", 042L, "42")
+ testboth("%o", -042L, "-42")
+ testboth("%o", float(042), "42")
-def test_exc(formatstr, args, exception, excmsg):
- try:
- testformat(formatstr, args)
- except exception, exc:
- if str(exc) == excmsg:
- if verbose:
- print "yes"
- else:
- if verbose: print 'no'
- print 'Unexpected ', exception, ':', repr(str(exc))
- except:
- if verbose: print 'no'
- print 'Unexpected exception'
- raise
- else:
- raise TestFailed, 'did not get expected exception: %s' % excmsg
+ # alternate float formatting
+ testformat('%g', 1.1, '1.1')
+ testformat('%#g', 1.1, '1.10000')
-test_exc('abc %a', 1, ValueError,
- "unsupported format character 'a' (0x61) at index 5")
-if have_unicode:
- test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError,
- "unsupported format character '?' (0x3000) at index 5")
+ # Regression test for http://bugs.python.org/issue15516.
+ class IntFails(object):
+ def __int__(self):
+ raise TestFailed
+ def __long__(self):
+ return 0
-test_exc('%d', '1', TypeError, "int argument required")
-test_exc('%g', '1', TypeError, "float argument required")
-test_exc('no format', '1', TypeError,
- "not all arguments converted during string formatting")
-test_exc('no format', u'1', TypeError,
- "not all arguments converted during string formatting")
-test_exc(u'no format', '1', TypeError,
- "not all arguments converted during string formatting")
-test_exc(u'no format', u'1', TypeError,
- "not all arguments converted during string formatting")
+ fst = IntFails()
+ testformat("%x", fst, '0')
-# for Jython, do we really need to support this? what's the use case
-# here! the problem in a nutshell is that it changes __oct__, __hex__
-# such that they don't return a string, but later on the exception
-# will occur anyway. so seems like a lot of work for no value
+ # Test exception for unknown format characters
+ if verbose:
+ print 'Testing exceptions'
-# class Foobar(long):
-# def __oct__(self):
-# # Returning a non-string should not blow up.
-# return self + 1
+ def test_exc(formatstr, args, exception, excmsg):
+ try:
+ testformat(formatstr, args)
+ except exception, exc:
+ if str(exc) == excmsg:
+ if verbose:
+ print "yes"
+ else:
+ if verbose: print 'no'
+ print 'Unexpected ', exception, ':', repr(str(exc))
+ except:
+ if verbose: print 'no'
+ print 'Unexpected exception'
+ raise
+ else:
+ raise TestFailed, 'did not get expected exception: %s' % excmsg
-#test_exc('%o', Foobar(), TypeError,
-# "expected string or Unicode object, long found")
+ test_exc('abc %a', 1, ValueError,
+ "unsupported format character 'a' (0x61) at index 5")
+ if have_unicode:
+ test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError,
+ "unsupported format character '?' (0x3000) at index 5")
-if sys.maxint == 2**31-1 and not is_jython:
- # crashes 2.2.1 and earlier:
- try:
- "%*d"%(sys.maxint, -127)
- except MemoryError:
- pass
- else:
- raise TestFailed, '"%*d"%(sys.maxint, -127) should fail'
+ test_exc('%d', '1', TypeError, "%d format: a number is required, not str")
+ test_exc('%g', '1', TypeError, "float argument required, not str")
+ test_exc('no format', '1', TypeError,
+ "not all arguments converted during string formatting")
+ test_exc('no format', u'1', TypeError,
+ "not all arguments converted during string formatting")
+ test_exc(u'no format', '1', TypeError,
+ "not all arguments converted during string formatting")
+ test_exc(u'no format', u'1', TypeError,
+ "not all arguments converted during string formatting")
+
+ # For Jython, we do not support this use case. The test aims at the,
+ # use of __oct__ within %o formatting of long. (Or __hex__ within %x
+ # formatting?) CPython does this for long (not int) and has dropped
+ # the idea again by v3. Jython's %o and %x are likewise direct.
+ class Foobar(long):
+ def __oct__(self):
+ # Returning a non-string should not blow up.
+ return self + 1
+
+ if not is_jython :
+ test_exc('%o', Foobar(), TypeError,
+ "expected string or Unicode object, long found")
+
+ if maxsize == 2**31-1:
+ # crashes 2.2.1 and earlier:
+ try:
+ "%*d"%(maxsize, -127)
+ except MemoryError:
+ pass
+ else:
+ raise TestFailed, '"%*d"%(maxsize, -127) should fail'
+
+def test_main():
+ test_support.run_unittest(FormatTest)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java
--- a/src/org/python/core/PyInteger.java
+++ b/src/org/python/core/PyInteger.java
@@ -7,6 +7,8 @@
import java.text.NumberFormat;
import java.util.Locale;
+import org.python.core.stringlib.IntegerFormatter;
+import org.python.core.stringlib.InternalFormat.Spec;
import org.python.core.stringlib.InternalFormatSpec;
import org.python.core.stringlib.InternalFormatSpecParser;
import org.python.expose.ExposedGet;
@@ -953,11 +955,8 @@
@ExposedMethod(doc = BuiltinDocs.int___oct___doc)
final PyString int___oct__() {
- if (getValue() < 0) {
- return new PyString("-0" + Integer.toString(getValue() * -1, 8));
- } else {
- return new PyString("0" + Integer.toString(getValue(), 8));
- }
+ // Use the prepared format specifier for octal.
+ return formatImpl(IntegerFormatter.OCT);
}
@Override
@@ -967,11 +966,21 @@
@ExposedMethod(doc = BuiltinDocs.int___hex___doc)
final PyString int___hex__() {
- if (getValue() < 0) {
- return new PyString("-0x" + Integer.toString(getValue() * -1, 16));
- } else {
- return new PyString("0x" + Integer.toString(getValue(), 16));
- }
+ // Use the prepared format specifier for hexadecimal.
+ return formatImpl(IntegerFormatter.HEX);
+ }
+
+ /**
+ * Common code used by the number-base conversion method __oct__ and __hex__.
+ *
+ * @param spec prepared format-specifier.
+ * @return converted value of this object
+ */
+ private PyString formatImpl(Spec spec) {
+ // Traditional formatter (%-format) because #o means "-0123" not "-0o123".
+ IntegerFormatter f = new IntegerFormatter.Traditional(spec);
+ f.format(value);
+ return new PyString(f.getResult());
}
@ExposedMethod(doc = BuiltinDocs.int___getnewargs___doc)
diff --git a/src/org/python/core/PyLong.java b/src/org/python/core/PyLong.java
--- a/src/org/python/core/PyLong.java
+++ b/src/org/python/core/PyLong.java
@@ -8,6 +8,9 @@
import java.math.BigDecimal;
import java.math.BigInteger;
+import org.python.core.stringlib.IntegerFormatter;
+import org.python.core.stringlib.InternalFormat;
+import org.python.core.stringlib.InternalFormat.Spec;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
@@ -977,14 +980,8 @@
@ExposedMethod(doc = BuiltinDocs.long___oct___doc)
final PyString long___oct__() {
- String s = PyInteger.toOctString(getValue());
- if (s.startsWith("-")) {
- return new PyString("-0" + s.substring(1, s.length()) + "L");
- } else if (s.startsWith("0")) {
- return new PyString(s + "L");
- } else {
- return new PyString("0" + s + "L");
- }
+ // Use the prepared format specifier for octal.
+ return formatImpl(IntegerFormatter.OCT);
}
@Override
@@ -994,12 +991,21 @@
@ExposedMethod(doc = BuiltinDocs.long___hex___doc)
final PyString long___hex__() {
- String s = PyInteger.toHexString(getValue());
- if (s.startsWith("-")) {
- return new PyString("-0x" + s.substring(1, s.length()) + "L");
- } else {
- return new PyString("0x" + s + "L");
- }
+ // Use the prepared format specifier for hexadecimal.
+ return formatImpl(IntegerFormatter.HEX);
+ }
+
+ /**
+ * Common code used by the number-base conversion method __oct__ and __hex__.
+ *
+ * @param spec prepared format-specifier.
+ * @return converted value of this object
+ */
+ private PyString formatImpl(Spec spec) {
+ // Traditional formatter (%-format) because #o means "-0123" not "-0o123".
+ IntegerFormatter f = new IntegerFormatter.Traditional(spec);
+ f.format(value).append('L');
+ return new PyString(f.getResult());
}
@ExposedMethod(doc = BuiltinDocs.long___str___doc)
diff --git a/src/org/python/core/PyString.java b/src/org/python/core/PyString.java
--- a/src/org/python/core/PyString.java
+++ b/src/org/python/core/PyString.java
@@ -9,6 +9,7 @@
import org.python.core.buffer.SimpleStringBuffer;
import org.python.core.stringlib.FieldNameIterator;
import org.python.core.stringlib.FloatFormatter;
+import org.python.core.stringlib.IntegerFormatter;
import org.python.core.stringlib.InternalFormat.Spec;
import org.python.core.stringlib.InternalFormatSpec;
import org.python.core.stringlib.InternalFormatSpecParser;
@@ -4106,211 +4107,89 @@
}
}
- private void checkPrecision(String type) {
- if (precision > 250) {
- // A magic number. Larger than in CPython.
- throw Py.OverflowError("formatted " + type + " is too long (precision too long?)");
- }
-
- }
-
/**
- * Format the argument interpreted as a long, using the argument's <code>__str__</code>,
- * <code>__oct__</code>, or <code>__hex__</code> method according to <code>type</code>. If v is
- * being treated as signed, the sign of v is transferred to {@link #negative} and the absolute
- * value is converted. The <code>altFlag</code> argument controls the appearance of a "0x" or
- * "0X" prefix in the hex case, or a "0" prefix in the octal case. The hexadecimal case, the
- * case of characters and digits will match the type ('x' meaning lowercase, 'X' meaning
- * uppercase).
+ * Return the argument as either a {@link PyInteger} or a {@link PyLong} according to its
+ * <code>__int__</code> method, or its <code>__long__</code> method. If the argument has neither
+ * method, or both raise an exception, we return the argument itself. The caller must check the
+ * return type.
*
* @param arg to convert
- * @param type one of 'o' for octal, 'x' or 'X' for hex, anything else calls
- * <code>arg.__str__</code>.
- * @param altFlag if true there will be a prefix
- * @return converted value as <code>String</code>
+ * @return PyInteger or PyLong if possible
*/
- private String formatLong(PyObject arg, char type, boolean altFlag) {
- // Convert using the appropriate type
- // XXX Results in behaviour divergent from CPython when any of the methods is overridden.
- PyString argAsString;
- switch (type) {
- case 'o':
- argAsString = arg.__oct__();
- break;
- case 'x':
- case 'X':
- argAsString = arg.__hex__();
- break;
- default:
- argAsString = arg.__str__();
- break;
- }
-
- checkPrecision("long");
- String s = argAsString.toString();
- int end = s.length();
- int ptr = 0;
-
- // In the hex case, the __hex__ return starts 0x
- // XXX (we assume, perhaps falsely)
- int numnondigits = 0;
- if (type == 'x' || type == 'X') {
- numnondigits = 2;
- }
-
- // Strip a "long" indicator
- if (s.endsWith("L")) {
- end--;
- }
-
- // Strip a possible sign to member negative
- negative = s.charAt(0) == '-';
- if (negative) {
- ptr++;
- }
-
- // The formatted number is s[ptr:end] and starts with numnondigits non-digits.
- int numdigits = end - numnondigits - ptr;
- if (!altFlag) {
- // We should have no "base tag" '0' or "0x" on the front.
- switch (type) {
- case 'o':
- // Strip the '0'
- if (numdigits > 1) {
- ++ptr;
- --numdigits;
- }
- break;
- case 'x':
- case 'X':
- // Strip the "0x"
- ptr += 2;
- numnondigits -= 2;
- break;
- }
- }
-
- // If necessary, add leading zeros to the numerical digits part.
- if (precision > numdigits) {
- // Recompose the formatted number in this buffer
- StringBuilder buf = new StringBuilder();
- // The base indicator prefix
- for (int i = 0; i < numnondigits; ++i) {
- buf.append(s.charAt(ptr++));
- }
- // The extra zeros
- for (int i = 0; i < precision - numdigits; i++) {
- buf.append('0');
- }
- // The previously known digits
- for (int i = 0; i < numdigits; i++) {
- buf.append(s.charAt(ptr++));
- }
- s = buf.toString();
- } else if (end < s.length() || ptr > 0) {
- // It's only necessary to extract the formatted number from s
- s = s.substring(ptr, end);
- }
-
- // And finally, deal with the case, so it matches x or X.
- switch (type) {
- case 'X':
- s = s.toUpperCase();
- break;
- }
- return s;
- }
-
- /**
- * Formats arg as an integer, with the specified radix. The integer value is obtained from the
- * result of <code>arg.__int__()</code>. <code>type</code> and <code>altFlag</code> are passed
- * to {@link #formatLong(PyObject, char, boolean)} in case the result is a PyLong.
- *
- * @param arg to convert
- * @param radix in which to express <code>arg</code>
- * @param unsigned true if required to interpret a 32-bit integer as unsigned ('u' legacy?).
- * @param type of conversion ('d', 'o', 'x', or 'X')
- * @param altFlag '#' present in format (causes "0x" prefix in hex, and '0' prefix in octal)
- * @return string form of the value
- */
- private String formatInteger(PyObject arg, int radix, boolean unsigned, char type,
- boolean altFlag) {
- PyObject argAsInt;
+ private PyObject asNumber(PyObject arg) {
if (arg instanceof PyInteger || arg instanceof PyLong) {
- argAsInt = arg;
+ // arg is already acceptable
+ return arg;
+
} else {
- // use __int__ to get an int (or long)
- if (arg instanceof PyFloat) {
- // safe to call __int__:
- argAsInt = arg.__int__();
+ // use __int__ or __long__to get an int (or long)
+ if (arg.getClass() == PyFloat.class) {
+ // A common case where it is safe to return arg.__int__()
+ return arg.__int__();
+
} else {
- // We can't simply call arg.__int__() because PyString implements
- // it without exposing it to python (i.e, str instances has no
- // __int__ attribute). So, we would support strings as arguments
- // for %d format, which is forbidden by CPython tests (on
- // test_format.py).
+ /*
+ * In general, we can't simply call arg.__int__() because PyString implements it
+ * without exposing it to python (str has no __int__). This would make str
+ * acceptacle to integer format specifiers, which is forbidden by CPython tests
+ * (test_format.py). PyString implements __int__ perhaps only to help the int
+ * constructor. Maybe that was a bad idea?
+ */
try {
- argAsInt = arg.__getattr__("__int__").__call__();
+ // Result is the result of arg.__int__() if that works
+ return arg.__getattr__("__int__").__call__();
} catch (PyException e) {
- // XXX: Swallow custom AttributeError throws from __int__ methods
- // No better alternative for the moment
- if (e.match(Py.AttributeError)) {
- throw Py.TypeError("int argument required");
- }
- throw e;
+ // Swallow the exception
+ }
+
+ // Try again with arg.__long__()
+ try {
+ // Result is the result of arg.__long__() if that works
+ return arg.__getattr__("__long__").__call__();
+ } catch (PyException e) {
+ // No __long__ defined (at Python level)
+ return arg;
}
}
}
- if (argAsInt instanceof PyInteger) {
- // This call does not provide the prefix and will be lowercase.
- return formatInteger(((PyInteger)argAsInt).getValue(), radix, unsigned);
- } else { // must be a PyLong (as per __int__ contract)
- // This call provides the base prefix and case-matches with 'x' or 'X'.
- return formatLong(argAsInt, type, altFlag);
- }
}
/**
- * Convert a 32-bit integer (as from a {@link PyInteger}) to characters, signed or unsigned. The
- * values is presented in a <code>long</code>. The string result is left-padded with zeros to
- * the stated {@link #precision}. If v is being treated as signed, the sign of v is transferred
- * to {@link #negative} and the absolute value is converted. Otherwise (unsigned case)
- * <code>0x100000000L + v</code> is converted. This method does not provide the '0' or "0x"
- * prefix, just the padded digit string.
+ * Return the argument as either a {@link PyFloat} according to its <code>__float__</code>
+ * method. If the argument has no such method, or it raises an exception, we return the argument
+ * itself. The caller must check the return type.
*
- * @param v value to convert
- * @param radix of conversion
- * @param unsigned if should be treated as unsigned
- * @return string form
+ * @param arg to convert
+ * @return PyFloat if possible
*/
- private String formatInteger(long v, int radix, boolean unsigned) {
- checkPrecision("integer");
- if (unsigned) {
- // If the high bit was set, this will have been sign-extended: correct that.
- if (v < 0) {
- v = 0x100000000l + v;
+ private PyObject asFloat(PyObject arg) {
+
+ if (arg instanceof PyFloat) {
+ // arg is already acceptable
+ return arg;
+
+ } else {
+ // use __float__ to get a float.
+ if (arg.getClass() == PyFloat.class) {
+ // A common case where it is safe to return arg.__int__()
+ return arg.__float__();
+
+ } else {
+ /*
+ * In general, we can't simply call arg.__float__() because PyString implements it
+ * without exposing it to python (str has no __float__). This would make str
+ * acceptacle to float format specifiers, which is forbidden by CPython tests
+ * (test_format.py). PyString implements __float__ perhaps only to help the float
+ * constructor. Maybe that was a bad idea?
+ */
+ try {
+ // Result is the result of arg.__float__() if that works
+ return arg.__getattr__("__float__").__call__();
+ } catch (PyException e) {
+ // No __float__ defined (at Python level)
+ return arg;
+ }
}
- } else {
- // If the high bit was set, the sign extension was correct, but we need sign + abs(v).
- if (v < 0) {
- negative = true;
- v = -v;
- }
- }
- // Use the method in java.lang.Long (lowercase, no prefix)
- String s = Long.toString(v, radix);
- // But zero pad to the requested precision
- while (s.length() < precision) {
- s = "0" + s;
- }
- return s;
- }
-
- private double asDouble(PyObject obj) {
- try {
- return obj.asDouble();
- } catch (PyException pye) {
- throw !pye.match(Py.TypeError) ? pye : Py.TypeError("float argument required");
}
}
@@ -4475,6 +4354,22 @@
fill = ' ';
}
+ // Encode as an InternalFormat.Spec
+ char fill2 = ' ';
+ char align = ljustFlag ? '<' : '>';
+ if (zeroFlag && !ljustFlag) {
+ // We only actually fill with zero if right-justifying
+ fill2 = '0';
+ // And then the fill comes after the sign.
+ align = '=';
+ }
+ char sign = signFlag ? '+' : (blankFlag ? ' ' : Spec.NONE);
+ int w = width;
+ Spec spec = new Spec(fill2, align, sign, altFlag, w, false, precision, c);
+
+ // Signal that the padding, sign, base prefix etc. have all been taken care of
+ boolean jobDone = false;
+
// Perform the type-specific formatting
switch (c) {
@@ -4503,102 +4398,65 @@
break;
- case 'i':
- case 'd':
- // Signed integer decimal. Note floats accepted.
- if (arg instanceof PyLong) {
- string = formatLong(arg, c, altFlag);
+ case 'd': // All integer formats (+case for X).
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'u': // Obsolete type identical to 'd'.
+ case 'i': // Compatibility with scanf().
+
+ // Format using this Spec the double form of the argument.
+ IntegerFormatter fi = new IntegerFormatter.Traditional(spec);
+
+ // Note various types accepted here as long as they have an __int__ method.
+ PyObject argAsNumber = asNumber(arg);
+
+ // We have to check what we got back..
+ if (argAsNumber instanceof PyInteger) {
+ fi.format(((PyInteger)argAsNumber).getValue());
+ } else if (argAsNumber instanceof PyLong) {
+ fi.format(((PyLong)argAsNumber).getValue());
} else {
- string = formatInteger(arg, 10, false, c, altFlag);
+ // It couldn't be converted, raise the error here
+ throw Py.TypeError("%" + c + " format: a number is required, not "
+ + arg.getType().fastGetName());
}
+
+ fi.pad();
+ string = fi.getResult();
+
+ // Suppress subsequent attempts to insert a correct sign, done already.
+ jobDone = true;
break;
- case 'u':
- // Obsolete type – it is identical to 'd'. (Why not identical here?)
- if (arg instanceof PyLong) {
- string = formatLong(arg, c, altFlag);
- } else if (arg instanceof PyInteger || arg instanceof PyFloat) {
- string = formatInteger(arg, 10, false, c, altFlag);
- } else {
- throw Py.TypeError("int argument required");
- }
- break;
-
- case 'o':
- // Signed octal value. Note floats accepted.
- if (arg instanceof PyLong) {
- // This call provides the base prefix '0' if altFlag.
- string = formatLong(arg, c, altFlag);
- } else if (arg instanceof PyInteger || arg instanceof PyFloat) {
- // This call does not provide the '0' prefix and will be lowercase ...
- // ... except where arg.__int__ returns PyLong, then it's like formatLong.
- string = formatInteger(arg, 8, false, c, altFlag);
- if (altFlag && string.charAt(0) != '0') {
- string = "0" + string;
- }
- } else {
- throw Py.TypeError("int argument required");
- }
- break;
-
- case 'x':
- // Signed hexadecimal (lowercase). Note floats accepted.
- if (arg instanceof PyLong) {
- // This call provides the base prefix "0x" if altFlag and case-matches c.
- string = formatLong(arg, c, altFlag);
- } else if (arg instanceof PyInteger || arg instanceof PyFloat) {
- // This call does not provide the "0x" prefix and will be lowercase.
- // ... except where arg.__int__ returns PyLong, then it's like formatLong.
- string = formatInteger(arg, 16, false, c, altFlag);
- string = string.toLowerCase();
- if (altFlag) {
- string = "0x" + string;
- }
- } else {
- throw Py.TypeError("int argument required");
- }
- break;
-
- case 'X':
- // Signed hexadecimal (uppercase). Note floats accepted.
- if (arg instanceof PyLong) {
- // This call provides the base prefix "0x" if altFlag and case-matches c.
- string = formatLong(arg, c, altFlag);
- } else if (arg instanceof PyInteger || arg instanceof PyFloat) {
- // This call does not provide the "0x" prefix and will be lowercase.
- // ... except where arg.__int__ returns PyLong, then it's like formatLong.
- string = formatInteger(arg, 16, false, c, altFlag);
- string = string.toUpperCase();
- if (altFlag) {
- string = "0X" + string;
- }
- } else {
- throw Py.TypeError("int argument required");
- }
- break;
-
- case 'e':
+ case 'e': // All floating point formats (+case).
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
- // All floating point formats (+case).
-
- // Convert the flags (local variables) to the form needed in the Spec object.
- char align = ljustFlag ? '<' : '>';
- char sign = signFlag ? '+' : (blankFlag ? ' ' : Spec.NONE);
- int w = Spec.UNSPECIFIED;
- Spec spec = new Spec(fill, align, sign, altFlag, w, false, precision, c);
// Format using this Spec the double form of the argument.
- FloatFormatter f = new FloatFormatter(spec);
- double v = asDouble(arg);
- f.format(v);
- string = f.getResult();
+ FloatFormatter ff = new FloatFormatter(spec);
+
+ // Note various types accepted here as long as they have a __float__ method.
+ PyObject argAsFloat = asFloat(arg);
+
+ // We have to check what we got back..
+ if (argAsFloat instanceof PyFloat) {
+ ff.format(((PyFloat)argAsFloat).getValue());
+ } else {
+ // It couldn't be converted, raise the error here
+ throw Py.TypeError("float argument required, not "
+ + arg.getType().fastGetName());
+ }
+
+ ff.pad();
+ string = ff.getResult();
// Suppress subsequent attempts to insert a correct sign, done already.
- signFlag = blankFlag = negative = false;
+ // signFlag = blankFlag = negative = false;
+ jobDone = true;
break;
case 'c':
@@ -4650,85 +4508,71 @@
* We have now dealt with the translation of the (absolute value of the) argument, in
* variable string[]. In the next sections we deal with sign, padding and base prefix.
*/
- int length = string.length();
- int skip = 0;
-
- // Decide how to represent the sign according to format and actual sign of argument.
- String signString = null;
- if (negative) {
- signString = "-";
+ if (jobDone) {
+ // Type-specific formatting has already taken care of all this.
+ buffer.append(string);
+
} else {
- if (signFlag) {
- signString = "+";
- } else if (blankFlag) {
- signString = " ";
+ // Legacy code still needed
+ int length = string.length();
+ int skip = 0;
+
+ // Decide how to represent the sign according to format and actual sign of argument.
+ String signString = null;
+ if (negative) {
+ signString = "-";
+ } else {
+ if (signFlag) {
+ signString = "+";
+ } else if (blankFlag) {
+ signString = " ";
+ }
}
- }
-
- // The width (from here on) will be the remaining width on the line.
- if (width < length) {
- width = length;
- }
-
- // Insert the sign in the buffer and adjust the width.
- if (signString != null) {
- if (fill != ' ') {
- // When the fill is not space, the sign comes before the fill.
- buffer.append(signString);
+
+ // The width (from here on) will be the remaining width on the line.
+ if (width < length) {
+ width = length;
}
- // Adjust width for sign.
- if (width > length) {
- width--;
+
+ // Insert the sign in the buffer and adjust the width.
+ if (signString != null) {
+ if (fill != ' ') {
+ // When the fill is not space, the sign comes before the fill.
+ buffer.append(signString);
+ }
+ // Adjust width for sign.
+ if (width > length) {
+ width--;
+ }
}
- }
-
- // Insert base prefix used with alternate mode for hexadecimal.
- if (altFlag && (c == 'x' || c == 'X')) {
- if (fill != ' ') {
- // When the fill is not space, this base prefix comes before the fill.
- buffer.append('0');
- buffer.append(c);
- skip += 2;
+
+ // Fill on the left of the item.
+ if (width > length && !ljustFlag) {
+ do {
+ buffer.append(fill);
+ } while (--width > length);
}
- // Adjust width for base prefix.
- width -= 2;
- if (width < 0) {
- width = 0;
+
+ // If the fill is spaces, we will have deferred the sign and hex base prefix
+ if (fill == ' ') {
+ if (signString != null) {
+ buffer.append(signString);
+ }
}
- length -= 2;
- }
-
- // Fill on the left of the item.
- if (width > length && !ljustFlag) {
- do {
- buffer.append(fill);
- } while (--width > length);
- }
-
- // If the fill is spaces, we will have deferred the sign and hex base prefix
- if (fill == ' ') {
- if (signString != null) {
- buffer.append(signString);
+
+ // Now append the converted argument.
+ if (skip > 0) {
+ // The string contains a hex-prefix, but we have already inserted one.
+ buffer.append(string.substring(skip));
+ } else {
+ buffer.append(string);
}
- if (altFlag && (c == 'x' || c == 'X')) {
- buffer.append('0');
- buffer.append(c);
- skip += 2;
+
+ // If this hasn't filled the space required, add right-padding.
+ while (--width >= length) {
+ buffer.append(' ');
}
}
-
- // Now append the converted argument.
- if (skip > 0) {
- // The string contains a hex-prefix, but we have already inserted one.
- buffer.append(string.substring(skip));
- } else {
- buffer.append(string);
- }
-
- // If this hasn't filled the space required, add right-padding.
- while (--width >= length) {
- buffer.append(' ');
- }
}
/*
diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java
--- a/src/org/python/core/__builtin__.java
+++ b/src/org/python/core/__builtin__.java
@@ -4,20 +4,20 @@
*/
package org.python.core;
-import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Map;
import org.python.antlr.base.mod;
+import org.python.core.stringlib.IntegerFormatter;
+import org.python.core.stringlib.InternalFormat;
+import org.python.core.stringlib.InternalFormat.Spec;
import org.python.core.util.ExtraMath;
import org.python.core.util.RelativeFile;
-import org.python.core.util.StringUtil;
import org.python.modules._functools._functools;
class BuiltinFunctions extends PyBuiltinFunctionSet {
@@ -768,7 +768,7 @@
/**
* Built-in Python function ord() applicable to the string-like types <code>str</code>,
* <code>bytearray</code>, <code>unicode</code>.
- *
+ *
* @param c string-like object of length 1
* @return ordinal value of character or byte value in
* @throws PyException (TypeError) if not a string-like type
@@ -1245,10 +1245,10 @@
PyObject[] args;
if (level < 0) {
// for backward compatibility provide only 4 arguments
- args = new PyObject[] {Py.newString(name), globals, locals,
+ args = new PyObject[] {Py.newString(name), globals, locals,
fromlist};
} else {
- args = new PyObject[] {Py.newString(name), globals, locals,
+ args = new PyObject[] {Py.newString(name), globals, locals,
fromlist, Py.newInteger(level)};
}
PyObject module = __import__.__call__(args);
@@ -1469,7 +1469,7 @@
endObject = useUnicode ? Py.newUnicode(end) : Py.newString(end);
}
- out.print(values, sepObject, endObject);
+ out.print(values, sepObject, endObject);
}
return Py.None;
}
@@ -1774,10 +1774,6 @@
public PyObject __call__(PyObject args[], String kwds[]) {
ArgParser ap = new ArgParser("bin", args, kwds, new String[] {"number"}, 1);
ap.noKeywords();
- PyObject number = ap.getPyObject(0);
-
- //XXX: this could be made more efficient by using a binary only formatter
- // instead of using generic formatting.
- return number.__format__(new PyString("#b"));
+ return IntegerFormatter.bin(ap.getPyObject(0));
}
}
diff --git a/src/org/python/core/stringlib/FloatFormatter.java b/src/org/python/core/stringlib/FloatFormatter.java
--- a/src/org/python/core/stringlib/FloatFormatter.java
+++ b/src/org/python/core/stringlib/FloatFormatter.java
@@ -18,6 +18,10 @@
/** The rounding mode dominant in the formatter. */
static final RoundingMode ROUND_PY = RoundingMode.HALF_EVEN;
+ /** Limit the size of results. */
+ // No-one needs more than log(Double.MAX_VALUE) - log2(Double.MIN_VALUE) = 1383 digits.
+ static final int MAX_PRECISION = 1400;
+
/** If it contains no decimal point, this length is zero, and 1 otherwise. */
private int lenPoint;
/** The length of the fractional part, right of the decimal point. */
@@ -160,6 +164,12 @@
// Precision defaults to 6 (or 12 for none-format)
int precision = spec.getPrecision(Spec.specified(spec.type) ? 6 : 12);
+ // Guard against excessive result precision
+ // XXX Possibly better raised before result is allocated/sized.
+ if (precision > MAX_PRECISION) {
+ throw precisionTooLarge("float");
+ }
+
/*
* By default, the prefix of a positive number is "", but the format specifier may override
* it, and the built-in type complex needs to override the format.
@@ -905,8 +915,8 @@
}
/**
- * Return the index in {@link #result} of the first letter. helper for {@link #uppercase()} and
- * {@link #getExponent()}
+ * Return the index in {@link #result} of the first letter. This is a helper for
+ * {@link #uppercase()} and {@link #getExponent()}
*/
private int indexOfMarker() {
return start + lenSign + lenWhole + lenPoint + lenFraction;
diff --git a/src/org/python/core/stringlib/IntegerFormatter.java b/src/org/python/core/stringlib/IntegerFormatter.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/core/stringlib/IntegerFormatter.java
@@ -0,0 +1,696 @@
+// Copyright (c) Jython Developers
+package org.python.core.stringlib;
+
+import java.math.BigInteger;
+
+import org.python.core.PyInteger;
+import org.python.core.PyLong;
+import org.python.core.PyObject;
+import org.python.core.PyString;
+import org.python.core.stringlib.InternalFormat.Spec;
+
+/**
+ * A class that provides the implementation of integer formatting. In a limited way, it acts like a
+ * StringBuilder to which text and one or more numbers may be appended, formatted according to the
+ * format specifier supplied at construction. These are ephemeral objects that are not, on their
+ * own, thread safe.
+ */
+public class IntegerFormatter extends InternalFormat.Formatter {
+
+ /**
+ * Construct the formatter from a specification. A reference is held to this specification, but
+ * it will not be modified by the actions of this class.
+ *
+ * @param spec parsed conversion specification
+ */
+ public IntegerFormatter(Spec spec) {
+ // Space for result is based on padded width, or precision, whole part & furniture.
+ this(spec, 12);
+ }
+
+ /**
+ * Construct the formatter from a specification and an explicit initial buffer capacity. A
+ * reference is held to this specification, but it will not be modified by the actions of this
+ * class.
+ *
+ * @param spec parsed conversion specification
+ * @param width expected for the formatted result
+ */
+ public IntegerFormatter(Spec spec, int width) {
+ super(spec, width);
+ }
+
+ /*
+ * Re-implement the text appends so they return the right type.
+ */
+ @Override
+ public IntegerFormatter append(char c) {
+ super.append(c);
+ return this;
+ }
+
+ @Override
+ public IntegerFormatter append(CharSequence csq) {
+ super.append(csq);
+ return this;
+ }
+
+ @Override
+ public IntegerFormatter append(CharSequence csq, int start, int end) //
+ throws IndexOutOfBoundsException {
+ super.append(csq, start, end);
+ return this;
+ }
+
+ /**
+ * Format a {@link BigInteger}, which is the implementation type of Jython <code>long</code>,
+ * according to the specification represented by this <code>IntegerFormatter</code>. The
+ * conversion type, and flags for grouping or base prefix are dealt with here. At the point this
+ * is used, we know the {@link #spec} is one of the integer types.
+ *
+ * @param value to convert
+ * @return this object
+ */
+ @SuppressWarnings("fallthrough")
+ public IntegerFormatter format(BigInteger value) {
+ try {
+ // Scratch all instance variables and start = result.length().
+ setStart();
+
+ // Different process for each format type.
+ switch (spec.type) {
+ case 'd':
+ case Spec.NONE:
+ case 'u':
+ case 'i':
+ // None format or d-format: decimal
+ format_d(value);
+ break;
+
+ case 'x':
+ // hexadecimal.
+ format_x(value, false);
+ break;
+
+ case 'X':
+ // HEXADECIMAL!
+ format_x(value, true);
+ break;
+
+ case 'o':
+ // Octal.
+ format_o(value);
+ break;
+
+ case 'b':
+ // Binary.
+ format_b(value);
+ break;
+
+ case 'n':
+ // Locale-sensitive version of d-format should be here.
+ format_d(value);
+ break;
+
+ default:
+ // Should never get here, since this was checked in caller.
+ throw unknownFormat(spec.type, "long");
+ }
+
+ // If required to, group the whole-part digits.
+ if (spec.grouping) {
+ groupDigits(3, ',');
+ }
+
+ return this;
+
+ } catch (OutOfMemoryError eme) {
+ // Most probably due to excessive precision.
+ throw precisionTooLarge("long");
+ }
+ }
+
+ /**
+ * Format the value as decimal (into {@link #result}). The option for mandatory sign is dealt
+ * with by reference to the format specification.
+ *
+ * @param value to convert
+ */
+ void format_d(BigInteger value) {
+ String number;
+ if (value.signum() < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(null);
+ number = value.negate().toString();
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(null);
+ number = value.toString();
+ }
+ appendNumber(number);
+ }
+
+ /**
+ * Format the value as hexadecimal (into {@link #result}), with the option of using upper-case
+ * or lower-case letters. The options for mandatory sign and for the presence of a base-prefix
+ * "0x" or "0X" are dealt with by reference to the format specification.
+ *
+ * @param value to convert
+ * @param upper if the hexadecimal should be upper case
+ */
+ void format_x(BigInteger value, boolean upper) {
+ String base = upper ? "0X" : "0x";
+ String number;
+ if (value.signum() < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(base);
+ number = toHexString(value.negate());
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(base);
+ number = toHexString(value);
+ }
+ // Append to result, case-shifted if necessary.
+ if (upper) {
+ number = number.toUpperCase();
+ }
+ appendNumber(number);
+ }
+
+ /**
+ * Format the value as octal (into {@link #result}). The options for mandatory sign and for the
+ * presence of a base-prefix "0o" are dealt with by reference to the format specification.
+ *
+ * @param value to convert
+ */
+ void format_o(BigInteger value) {
+ String base = "0o";
+ String number;
+ if (value.signum() < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(base);
+ number = toOctalString(value.negate());
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(base);
+ number = toOctalString(value);
+ }
+ // Append to result.
+ appendNumber(number);
+ }
+
+ /**
+ * Format the value as binary (into {@link #result}). The options for mandatory sign and for the
+ * presence of a base-prefix "0b" are dealt with by reference to the format specification.
+ *
+ * @param value to convert
+ */
+ void format_b(BigInteger value) {
+ String base = "0b";
+ String number;
+ if (value.signum() < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(base);
+ number = toBinaryString(value.negate());
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(base);
+ number = toBinaryString(value);
+ }
+ // Append to result.
+ appendNumber(number);
+ }
+
+ /**
+ * Format an integer according to the specification represented by this
+ * <code>IntegerFormatter</code>. The conversion type, and flags for grouping or base prefix are
+ * dealt with here. At the point this is used, we know the {@link #spec} is one of the integer
+ * types.
+ *
+ * @param value to convert
+ * @return this object
+ */
+ @SuppressWarnings("fallthrough")
+ public IntegerFormatter format(int value) {
+ try {
+ // Scratch all instance variables and start = result.length().
+ setStart();
+
+ // Different process for each format type.
+ switch (spec.type) {
+ case 'd':
+ case Spec.NONE:
+ case 'u':
+ case 'i':
+ // None format or d-format: decimal
+ format_d(value);
+ break;
+
+ case 'x':
+ // hexadecimal.
+ format_x(value, false);
+ break;
+
+ case 'X':
+ // HEXADECIMAL!
+ format_x(value, true);
+ break;
+
+ case 'o':
+ // Octal.
+ format_o(value);
+ break;
+
+ case 'b':
+ // Binary.
+ format_b(value);
+ break;
+
+ case 'n':
+ // Locale-sensitive version of d-format should be here.
+ format_d(value);
+ break;
+
+ default:
+ // Should never get here, since this was checked in caller.
+ throw unknownFormat(spec.type, "integer");
+ }
+
+ // If required to, group the whole-part digits.
+ if (spec.grouping) {
+ groupDigits(3, ',');
+ }
+
+ return this;
+ } catch (OutOfMemoryError eme) {
+ // Most probably due to excessive precision.
+ throw precisionTooLarge("integer");
+ }
+ }
+
+ /**
+ * Format the value as hexadecimal (into {@link #result}), with the option of using upper-case
+ * or lower-case letters. The options for mandatory sign and for the presence of a base-prefix
+ * "0x" or "0X" are dealt with by reference to the format specification.
+ *
+ * @param value to convert
+ * @param upper if the hexadecimal should be upper case
+ */
+ void format_x(int value, boolean upper) {
+ String base = upper ? "0X" : "0x";
+ String number;
+ if (value < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(base);
+ number = Integer.toHexString(-value);
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(base);
+ number = Integer.toHexString(value);
+ }
+ // Append to result, case-shifted if necessary.
+ if (upper) {
+ number = number.toUpperCase();
+ }
+ appendNumber(number);
+ }
+
+ /**
+ * Format the value as decimal (into {@link #result}). The option for mandatory sign is dealt
+ * with by reference to the format specification.
+ *
+ * @param value to convert
+ */
+ void format_d(int value) {
+ String number;
+ if (value < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(null);
+ number = Integer.toString(-value);
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(null);
+ number = Integer.toString(value);
+ }
+ appendNumber(number);
+ }
+
+ /**
+ * Format the value as octal (into {@link #result}). The options for mandatory sign and for the
+ * presence of a base-prefix "0o" are dealt with by reference to the format specification.
+ *
+ * @param value to convert
+ */
+ void format_o(int value) {
+ String base = "0o";
+ String number;
+ if (value < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(base);
+ number = Integer.toOctalString(-value);
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(base);
+ number = Integer.toOctalString(value);
+ }
+ // Append to result.
+ appendNumber(number);
+ }
+
+ /**
+ * Format the value as binary (into {@link #result}). The options for mandatory sign and for the
+ * presence of a base-prefix "0b" are dealt with by reference to the format specification.
+ *
+ * @param value to convert
+ */
+ void format_b(int value) {
+ String base = "0b";
+ String number;
+ if (value < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(base);
+ number = Integer.toBinaryString(-value);
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(base);
+ number = Integer.toBinaryString(value);
+ }
+ // Append to result.
+ appendNumber(number);
+ }
+
+ /**
+ * Append to {@link #result} buffer a sign (if one is specified for positive numbers) and, in
+ * alternate mode, the base marker provided. The sign and base marker are together considered to
+ * be the "sign" of the converted number, spanned by {@link #lenSign}. This is relevant when we
+ * come to insert padding.
+ *
+ * @param base marker "0x" or "0X" for hex, "0o" for octal, "0b" for binary, "" or
+ * <code>null</code> for decimal.
+ */
+ final void positiveSign(String base) {
+ // Does the format specify a sign for positive values?
+ char sign = spec.sign;
+ if (Spec.specified(sign)) {
+ append(sign);
+ lenSign = 1;
+ }
+ // Does the format call for a base prefix?
+ if (base != null && spec.alternate) {
+ append(base);
+ lenSign += base.length();
+ }
+ }
+
+ /**
+ * Append to {@link #result} buffer a minus sign and, in alternate mode, the base marker
+ * provided. The sign and base marker are together considered to be the "sign" of the converted
+ * number, spanned by {@link #lenSign}. This is relevant when we come to insert padding.
+ *
+ * @param base marker ("0x" or "0X" for hex, "0" for octal, <code>null</code> or "" for decimal.
+ */
+ final void negativeSign(String base) {
+ // Insert a minus sign unconditionally.
+ append('-');
+ lenSign = 1;
+ // Does the format call for a base prefix?
+ if (base != null && spec.alternate) {
+ append(base);
+ lenSign += base.length();
+ }
+ }
+
+ /**
+ * Append a string (number) to {@link #result} and set {@link #lenWhole} to its length .
+ *
+ * @param number to append
+ */
+ void appendNumber(String number) {
+ lenWhole = number.length();
+ append(number);
+ }
+
+ // For hex-conversion by lookup
+ private static final String LOOKUP = "0123456789abcdef";
+
+ /**
+ * A more efficient algorithm for generating a hexadecimal representation of a byte array.
+ * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and,
+ * consequently, is implemented using expensive mathematical operations.
+ *
+ * @param value the value to generate a hexadecimal string from
+ * @return the hexadecimal representation of value, with "-" sign prepended if necessary
+ */
+ private static final String toHexString(BigInteger value) {
+ int signum = value.signum();
+
+ // obvious shortcut
+ if (signum == 0) {
+ return "0";
+ }
+
+ // we want to work in absolute numeric value (negative sign is added afterward)
+ byte[] input = value.abs().toByteArray();
+ StringBuilder sb = new StringBuilder(input.length * 2);
+
+ int b;
+ for (int i = 0; i < input.length; i++) {
+ b = input[i] & 0xFF;
+ sb.append(LOOKUP.charAt(b >> 4));
+ sb.append(LOOKUP.charAt(b & 0x0F));
+ }
+
+ // before returning the char array as string, remove leading zeroes, but not the last one
+ String result = sb.toString().replaceFirst("^0+(?!$)", "");
+ return signum < 0 ? "-" + result : result;
+ }
+
+ /**
+ * A more efficient algorithm for generating an octal representation of a byte array.
+ * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and,
+ * consequently, is implemented using expensive mathematical operations.
+ *
+ * @param value the value to generate an octal string from
+ * @return the octal representation of value, with "-" sign prepended if necessary
+ */
+ private static final String toOctalString(BigInteger value) {
+ int signum = value.signum();
+
+ // obvious shortcut
+ if (signum == 0) {
+ return "0";
+ }
+
+ byte[] input = value.abs().toByteArray();
+ if (input.length < 3) {
+ return value.toString(8);
+ }
+
+ StringBuilder sb = new StringBuilder(input.length * 3);
+
+ // working backwards, three bytes at a time
+ int threebytes;
+ int trip1, trip2, trip3; // most, middle, and least significant bytes in the triplet
+ for (int i = input.length - 1; i >= 0; i -= 3) {
+ trip3 = input[i] & 0xFF;
+ trip2 = ((i - 1) >= 0) ? (input[i - 1] & 0xFF) : 0x00;
+ trip1 = ((i - 2) >= 0) ? (input[i - 2] & 0xFF) : 0x00;
+ threebytes = trip3 | (trip2 << 8) | (trip1 << 16);
+
+ // convert the three-byte value into an eight-character octal string
+ for (int j = 0; j < 8; j++) {
+ sb.append(LOOKUP.charAt((threebytes >> (j * 3)) & 0x000007));
+ }
+ }
+
+ String result = sb.reverse().toString().replaceFirst("^0+(?!%)", "");
+ return signum < 0 ? "-" + result : result;
+ }
+
+ /**
+ * A more efficient algorithm for generating a binary representation of a byte array.
+ * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and,
+ * consequently, is implemented using expensive mathematical operations.
+ *
+ * @param value the value to generate a binary string from
+ * @return the binary representation of value, with "-" sign prepended if necessary
+ */
+ private static final String toBinaryString(BigInteger value) {
+ int signum = value.signum();
+
+ // obvious shortcut
+ if (signum == 0) {
+ return "0";
+ }
+
+ // we want to work in absolute numeric value (negative sign is added afterward)
+ byte[] input = value.abs().toByteArray();
+ StringBuilder sb = new StringBuilder(value.bitCount());
+
+ int b;
+ for (int i = 0; i < input.length; i++) {
+ b = input[i] & 0xFF;
+ for (int bit = 7; bit >= 0; bit--) {
+ sb.append(((b >> bit) & 0x1) > 0 ? "1" : "0");
+ }
+ }
+
+ // before returning the char array as string, remove leading zeroes, but not the last one
+ String result = sb.toString().replaceFirst("^0+(?!$)", "");
+ return signum < 0 ? "-" + result : result;
+ }
+
+ /** Format specification used by bin(). */
+ public static final Spec BIN = InternalFormat.fromText("#b");
+
+ /** Format specification used by oct(). */
+ public static final Spec OCT = InternalFormat.fromText("#o");
+
+ /** Format specification used by hex(). */
+ public static final Spec HEX = InternalFormat.fromText("#x");
+
+ /**
+ * Convert the object to binary according to the conventions of Python built-in
+ * <code>bin()</code>. The object's __index__ method is called, and is responsible for raising
+ * the appropriate error (which the base {@link PyObject#__index__()} does).
+ *
+ * @param number to convert
+ * @return PyString converted result
+ */
+ // Follow this pattern in Python 3, where objects no longer have __hex__, __oct__ members.
+ public static PyString bin(PyObject number) {
+ return formatNumber(number, BIN);
+ }
+
+ /**
+ * Convert the object according to the conventions of Python built-in <code>hex()</code>, or
+ * <code>oct()</code>. The object's <code>__index__</code> method is called, and is responsible
+ * for raising the appropriate error (which the base {@link PyObject#__index__()} does).
+ *
+ * @param number to convert
+ * @return PyString converted result
+ */
+ public static PyString formatNumber(PyObject number, Spec spec) {
+ number = number.__index__();
+ IntegerFormatter f = new IntegerFormatter(spec);
+ if (number instanceof PyInteger) {
+ f.format(((PyInteger)number).getValue());
+ } else {
+ f.format(((PyLong)number).getValue());
+ }
+ return new PyString(f.getResult());
+ }
+
+ /**
+ * A minor variation on {@link IntegerFormatter} to handle "traditional" %-formatting. The
+ * difference is in the formatting octal in "alternate" mode (0 and 0123, not 0o0 and 0o123).
+ */
+ public static class Traditional extends IntegerFormatter {
+
+ /**
+ * Construct the formatter from a specification. A reference is held to this specification,
+ * but it will not be modified by the actions of this class.
+ *
+ * @param spec parsed conversion specification
+ */
+ public Traditional(Spec spec) {
+ super(spec);
+ }
+
+ /**
+ * Construct the formatter from a specification and an explicit initial buffer capacity. A
+ * reference is held to this specification, but it will not be modified by the actions of
+ * this class.
+ *
+ * @param spec parsed conversion specification
+ * @param width expected for the formatted result
+ */
+ public Traditional(Spec spec, int width) {
+ super(spec, width);
+ }
+
+ /**
+ * Format the value as octal (into {@link #result}). The options for mandatory sign and for
+ * the presence of a base-prefix "0" are dealt with by reference to the format
+ * specification.
+ *
+ * @param value to convert
+ */
+ @Override
+ void format_o(BigInteger value) {
+ String number;
+ int signum = value.signum();
+ if (signum < 0) {
+ // Negative value: deal with sign and base, and convert magnitude.
+ negativeSign(null);
+ number = toOctalString(value.negate());
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(null);
+ number = toOctalString(value);
+ }
+ // Append to result.
+ appendOctalNumber(number);
+ }
+
+ /**
+ * Format the value as octal (into {@link #result}). The options for mandatory sign and for
+ * the presence of a base-prefix "0" are dealt with by reference to the format
+ * specification.
+ *
+ * @param value to convert
+ */
+ @Override
+ void format_o(int value) {
+ String number;
+ if (value < 0) {
+ // Negative value: deal with sign and convert magnitude.
+ negativeSign(null);
+ number = Integer.toOctalString(-value);
+ } else {
+ // Positive value: deal with sign, base and magnitude.
+ positiveSign(null);
+ number = Integer.toOctalString(value);
+ }
+ // Append to result.
+ appendOctalNumber(number);
+ }
+
+ /**
+ * Append a string (number) to {@link #result}, but insert leading zeros first in order
+ * that, on return, the whole-part length #lenWhole should be no less than the precision.
+ *
+ * @param number to append
+ */
+ @Override
+ void appendNumber(String number) {
+ int n, p = spec.getPrecision(0);
+ for (n = number.length(); n < p; n++) {
+ result.append('0');
+ }
+ lenWhole = n;
+ append(number);
+ }
+
+ /**
+ * Append a string (number) to {@link #result}, but insert leading zeros first in order
+ * that, on return, the whole-part length #lenWhole should be no less than the precision.
+ * Octal numbers must begin with a zero if <code>spec.alternate==true</code>, so if the
+ * number passed in does not start with a zero, at least one will be inserted.
+ *
+ * @param number to append
+ */
+ void appendOctalNumber(String number) {
+ int n = number.length(), p = spec.getPrecision(0);
+ if (spec.alternate && number.charAt(0) != '0' && n >= p) {
+ p = n + 1;
+ }
+ for (; n < p; n++) {
+ result.append('0');
+ }
+ lenWhole = n;
+ append(number);
+ }
+
+ }
+}
diff --git a/src/org/python/core/stringlib/InternalFormat.java b/src/org/python/core/stringlib/InternalFormat.java
--- a/src/org/python/core/stringlib/InternalFormat.java
+++ b/src/org/python/core/stringlib/InternalFormat.java
@@ -32,7 +32,7 @@
/** The number we are working on floats at the end of the result, and starts here. */
protected int start;
- /** If it contains no sign, this length is zero, and 1 otherwise. */
+ /** If it contains no sign, this length is zero, and >0 otherwise. */
protected int lenSign;
/** The length of the whole part (to left of the decimal point or exponent) */
protected int lenWhole;
@@ -98,7 +98,7 @@
* receive a new one.
*/
protected void reset() {
- // Clear the variable describing the latest object in result.
+ // Clear the variables describing the latest object in result.
lenSign = lenWhole = 0;
}
@@ -221,13 +221,13 @@
* greater than the current length.
* <p>
* When the padding method has decided that that it needs to add n padding characters, it
- * will affect {@link #start} or {@link #lenSign} as follows.
+ * will affect {@link #start} or {@link #lenWhole} as follows.
* <table border style>
* <tr>
* <th>align</th>
* <th>meaning</th>
* <th>start</th>
- * <th>lenSign</th>
+ * <th>lenWhole</th>
* <th>result.length()</th>
* </tr>
* <tr>
@@ -259,11 +259,10 @@
* <td>+n</td>
* </tr>
* </table>
- * Note that we may have converted more than one value into the result buffer (for example
- * when formatting a complex number). The pointer <code>start</code> is at the start of the
- * last number converted. Padding with zeros, and the "pad after sign" mode, will produce a
- * result you probably don't want. It is up to the client to disallow this (which
- * <code>complex</code> does).
+ * Note that in the "pad after sign" mode, only the last number into the buffer receives the
+ * padding. This padding gets incorporated into the whole part of the number. (In other
+ * modes, the padding is around the whole buffer.) When this would not be appropriate, it is
+ * up to the client to disallow this (which <code>complex</code> does).
*
* @return this object
*/
@@ -345,7 +344,7 @@
* </pre>
*
* The padding has increased the overall length of the result to the target width. About one
- * in three call to this method adds one to the width, because the whole part cannot start
+ * in three calls to this method adds one to the width, because the whole part cannot start
* with a comma.
*
* <pre>
@@ -355,9 +354,6 @@
* '-<b>0</b>,000,000,001,200,000,000.0000'
* </pre>
*
- * Insert grouping characters (conventionally commas) into the whole part of the number.
- * {@link #lenWhole} will increase correspondingly.
- *
* @param groupSize normally 3.
* @param comma or some other character to use as a separator.
*/
@@ -386,10 +382,9 @@
* Suppose the format call was format(-12e8, "0=30,.4f"). At the beginning, we had
* something like this in result: . [-|000000000001,200,000,000|.|0000||]
*
- * And now, result looks like this: [-|0000,000,001,200,000,000|.|0000||] in which
- * the first zero is wrong as it stands, nor can it just be over-written with a
- * comma. We have to insert another zero, even though this makes the result longer
- * than we were given.
+ * And now, result looks like this: [-|,000,000,001,200,000,000|.|0000||] in which
+ * the first comma is wrong, but so would be a zero. We have to insert another zero,
+ * even though this makes the result longer than we were asked for.
*/
result.insert(firstZero, '0');
lenWhole += 1;
@@ -457,6 +452,19 @@
return Py.ValueError(msg);
}
+ /**
+ * Convenience method returning a {@link Py#OverflowError} reporting:
+ * <p>
+ * <code>"formatted "+type+" is too long (precision too large?)"</code>
+ *
+ * @param type of formatting ("integer", "float")
+ * @return exception to throw
+ */
+ public static PyException precisionTooLarge(String type) {
+ String msg = "formatted " + type + " is too long (precision too large?)";
+ return Py.OverflowError(msg);
+ }
+
}
/**
--
Repository URL: http://hg.python.org/jython
More information about the Jython-checkins
mailing list