[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