Author: Armin Rigo <[email protected]>
Branch: stdlib-2.7.13
Changeset: r89155:fc358e88d939
Date: 2016-12-18 18:28 +0100
http://bitbucket.org/pypy/pypy/changeset/fc358e88d939/

Log:    Update to 2.7.13's handling of ``'%d' % x``, which gives completely
        different results if x is a subclass of 'long' or a subclass of
        'int'

diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py
--- a/pypy/objspace/std/formatting.py
+++ b/pypy/objspace/std/formatting.py
@@ -547,11 +547,13 @@
     # make sure that w_value is a wrapped float
     return space.float(w_value)
 
-def format_num_helper_generator(fmt, digits):
+def format_num_helper_generator(fmt, digits, method, remove_prefix=''):
     def format_num_helper(space, w_value):
-        try:
+        if (not space.isinstance_w(w_value, space.w_int) and
+            not space.isinstance_w(w_value, space.w_long)):
+          try:
             w_value = maybe_int(space, w_value)
-        except OperationError:
+          except OperationError:
             try:
                 w_value = space.long(w_value)
             except OperationError as operr:
@@ -561,17 +563,27 @@
                         "%s format: a number is required, not %T", fmt, 
w_value)
                 else:
                     raise
-        try:
+        if space.isinstance_w(w_value, space.w_long):
+            text = space.str_w(space.call_method(w_value, method))
+            skip_left = 0
+            skip_right = len(text)
+            if remove_prefix:
+                if not text.startswith(remove_prefix):
+                    raise oefmt(space.w_ValueError,
+                                "%s format: invalid result of %s (type=%T)",
+                                fmt, method, w_value)
+                skip_left = len(remove_prefix)
+            if text.endswith('L'):
+                skip_right = len(text) - 1
+                assert skip_right >= 0
+            return text[skip_left : skip_right]
+        else:
             value = space.int_w(w_value)
             return fmt % (value,)
-        except OperationError as operr:
-            if not operr.match(space, space.w_OverflowError):
-                raise
-            num = space.bigint_w(w_value)
-            return num.format(digits)
     return func_with_new_name(format_num_helper,
                               'base%d_num_helper' % len(digits))
 
-int_num_helper = format_num_helper_generator('%d', '0123456789')
-oct_num_helper = format_num_helper_generator('%o', '01234567')
-hex_num_helper = format_num_helper_generator('%x', '0123456789abcdef')
+int_num_helper = format_num_helper_generator('%d', '0123456789', '__str__')
+oct_num_helper = format_num_helper_generator('%o', '01234567', '__oct__', '0')
+hex_num_helper = format_num_helper_generator('%x', '0123456789abcdef',
+                                             '__hex__', '0x')
diff --git a/pypy/objspace/std/test/test_stringformat.py 
b/pypy/objspace/std/test/test_stringformat.py
--- a/pypy/objspace/std/test/test_stringformat.py
+++ b/pypy/objspace/std/test/test_stringformat.py
@@ -137,6 +137,57 @@
         sl = SubLong(l)
         assert '%d' % sl == '4800000000'
 
+    def test_format_subclass_with_str(self):
+        import sys
+        if sys.version_info < (2, 7, 13):
+            skip("CPython gives SystemError before 2.7.13")
+            #...and behaves inconsistently in 2.7.13, but we reproduce that
+
+        class SubInt2(int):
+            def __str__(self):
+                assert False, "not called"
+            def __hex__(self):
+                assert False, "not called"
+            def __oct__(self):
+                assert False, "not called"
+            def __int__(self):
+                assert False, "not called"
+            def __long__(self):
+                assert False, "not called"
+        sl = SubInt2(123)
+        assert '%i' % sl == '123'
+        assert '%u' % sl == '123'
+        assert '%d' % sl == '123'
+        assert '%x' % sl == '7b'
+        assert '%X' % sl == '7B'
+        assert '%o' % sl == '173'
+
+        class SubLong2(long):
+            def __str__(self):
+                return 'Xx'
+            def __hex__(self):
+                return extra_stuff + '0xYy' + extra_tail
+            def __oct__(self):
+                return extra_stuff + '0Zz' + extra_tail
+            def __int__(self):
+                assert False, "not called"
+            def __long__(self):
+                assert False, "not called"
+        sl = SubLong2(123)
+        extra_stuff = ''
+        for extra_tail in ['', 'l', 'L']:
+            x = '%i' % sl
+            assert x == 'Xx'
+            assert '%u' % sl == 'Xx'
+            assert '%d' % sl == 'Xx'
+            assert '%x' % sl == ('Yyl' if extra_tail == 'l' else 'Yy')
+            assert '%X' % sl == ('YYL' if extra_tail == 'l' else 'YY')
+            assert '%o' % sl == ('Zzl' if extra_tail == 'l' else 'Zz')
+        extra_stuff = '??'
+        raises(ValueError, "'%x' % sl")
+        raises(ValueError, "'%X' % sl")
+        raises(ValueError, "'%o' % sl")
+
     def test_format_list(self):
         l = [1,2]
         assert '<[1, 2]>' == '<%s>' % l
@@ -202,7 +253,8 @@
             def __long__(self):
                 return 0L
 
-        assert "%x" % IntFails() == '0'
+        x = "%x" % IntFails()
+        assert x == '0'
 
     def test_formatting_huge_precision(self):
         prec = 2**31
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to