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