Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 31a6554

Browse files
Issue #17576: Deprecation warning emitted now when __int__() or __index__()
return not int instance. Introduced _PyLong_FromNbInt() and refactored PyLong_As*() functions.
1 parent acd1730 commit 31a6554

File tree

7 files changed

+312
-186
lines changed

7 files changed

+312
-186
lines changed

Include/longobject.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
152152
unsigned char* bytes, size_t n,
153153
int little_endian, int is_signed);
154154

155+
/* _PyLong_FromNbInt: Convert the given object to a PyLongObject
156+
using the nb_int slot, if available. Raise TypeError if either the
157+
nb_int slot is not available or the result of the call to nb_int
158+
returns something not of type int.
159+
*/
160+
PyAPI_FUNC(PyLongObject *)_PyLong_FromNbInt(PyObject *);
155161

156162
/* _PyLong_Format: Convert the long to a string object with given base,
157163
appending a base prefix of 0[box] if base is 2, 8 or 16. */

Lib/test/test_getargs2.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,34 @@ class Int:
5050
def __int__(self):
5151
return 99
5252

53+
class IntSubclass(int):
54+
def __int__(self):
55+
return 99
56+
57+
class BadInt:
58+
def __int__(self):
59+
return 1.0
60+
61+
class BadInt2:
62+
def __int__(self):
63+
return True
64+
65+
class BadInt3(int):
66+
def __int__(self):
67+
return True
68+
69+
5370
class Unsigned_TestCase(unittest.TestCase):
5471
def test_b(self):
5572
from _testcapi import getargs_b
5673
# b returns 'unsigned char', and does range checking (0 ... UCHAR_MAX)
5774
self.assertRaises(TypeError, getargs_b, 3.14)
5875
self.assertEqual(99, getargs_b(Int()))
76+
self.assertEqual(0, getargs_b(IntSubclass()))
77+
self.assertRaises(TypeError, getargs_b, BadInt())
78+
with self.assertWarns(DeprecationWarning):
79+
self.assertEqual(1, getargs_b(BadInt2()))
80+
self.assertEqual(0, getargs_b(BadInt3()))
5981

6082
self.assertRaises(OverflowError, getargs_b, -1)
6183
self.assertEqual(0, getargs_b(0))
@@ -70,6 +92,11 @@ def test_B(self):
7092
# B returns 'unsigned char', no range checking
7193
self.assertRaises(TypeError, getargs_B, 3.14)
7294
self.assertEqual(99, getargs_B(Int()))
95+
self.assertEqual(0, getargs_B(IntSubclass()))
96+
self.assertRaises(TypeError, getargs_B, BadInt())
97+
with self.assertWarns(DeprecationWarning):
98+
self.assertEqual(1, getargs_B(BadInt2()))
99+
self.assertEqual(0, getargs_B(BadInt3()))
73100

74101
self.assertEqual(UCHAR_MAX, getargs_B(-1))
75102
self.assertEqual(0, getargs_B(0))
@@ -84,6 +111,11 @@ def test_H(self):
84111
# H returns 'unsigned short', no range checking
85112
self.assertRaises(TypeError, getargs_H, 3.14)
86113
self.assertEqual(99, getargs_H(Int()))
114+
self.assertEqual(0, getargs_H(IntSubclass()))
115+
self.assertRaises(TypeError, getargs_H, BadInt())
116+
with self.assertWarns(DeprecationWarning):
117+
self.assertEqual(1, getargs_H(BadInt2()))
118+
self.assertEqual(0, getargs_H(BadInt3()))
87119

88120
self.assertEqual(USHRT_MAX, getargs_H(-1))
89121
self.assertEqual(0, getargs_H(0))
@@ -99,6 +131,11 @@ def test_I(self):
99131
# I returns 'unsigned int', no range checking
100132
self.assertRaises(TypeError, getargs_I, 3.14)
101133
self.assertEqual(99, getargs_I(Int()))
134+
self.assertEqual(0, getargs_I(IntSubclass()))
135+
self.assertRaises(TypeError, getargs_I, BadInt())
136+
with self.assertWarns(DeprecationWarning):
137+
self.assertEqual(1, getargs_I(BadInt2()))
138+
self.assertEqual(0, getargs_I(BadInt3()))
102139

103140
self.assertEqual(UINT_MAX, getargs_I(-1))
104141
self.assertEqual(0, getargs_I(0))
@@ -115,6 +152,10 @@ def test_k(self):
115152
# it does not accept float, or instances with __int__
116153
self.assertRaises(TypeError, getargs_k, 3.14)
117154
self.assertRaises(TypeError, getargs_k, Int())
155+
self.assertEqual(0, getargs_k(IntSubclass()))
156+
self.assertRaises(TypeError, getargs_k, BadInt())
157+
self.assertRaises(TypeError, getargs_k, BadInt2())
158+
self.assertEqual(0, getargs_k(BadInt3()))
118159

119160
self.assertEqual(ULONG_MAX, getargs_k(-1))
120161
self.assertEqual(0, getargs_k(0))
@@ -131,6 +172,11 @@ def test_h(self):
131172
# h returns 'short', and does range checking (SHRT_MIN ... SHRT_MAX)
132173
self.assertRaises(TypeError, getargs_h, 3.14)
133174
self.assertEqual(99, getargs_h(Int()))
175+
self.assertEqual(0, getargs_h(IntSubclass()))
176+
self.assertRaises(TypeError, getargs_h, BadInt())
177+
with self.assertWarns(DeprecationWarning):
178+
self.assertEqual(1, getargs_h(BadInt2()))
179+
self.assertEqual(0, getargs_h(BadInt3()))
134180

135181
self.assertRaises(OverflowError, getargs_h, SHRT_MIN-1)
136182
self.assertEqual(SHRT_MIN, getargs_h(SHRT_MIN))
@@ -145,6 +191,11 @@ def test_i(self):
145191
# i returns 'int', and does range checking (INT_MIN ... INT_MAX)
146192
self.assertRaises(TypeError, getargs_i, 3.14)
147193
self.assertEqual(99, getargs_i(Int()))
194+
self.assertEqual(0, getargs_i(IntSubclass()))
195+
self.assertRaises(TypeError, getargs_i, BadInt())
196+
with self.assertWarns(DeprecationWarning):
197+
self.assertEqual(1, getargs_i(BadInt2()))
198+
self.assertEqual(0, getargs_i(BadInt3()))
148199

149200
self.assertRaises(OverflowError, getargs_i, INT_MIN-1)
150201
self.assertEqual(INT_MIN, getargs_i(INT_MIN))
@@ -159,6 +210,11 @@ def test_l(self):
159210
# l returns 'long', and does range checking (LONG_MIN ... LONG_MAX)
160211
self.assertRaises(TypeError, getargs_l, 3.14)
161212
self.assertEqual(99, getargs_l(Int()))
213+
self.assertEqual(0, getargs_l(IntSubclass()))
214+
self.assertRaises(TypeError, getargs_l, BadInt())
215+
with self.assertWarns(DeprecationWarning):
216+
self.assertEqual(1, getargs_l(BadInt2()))
217+
self.assertEqual(0, getargs_l(BadInt3()))
162218

163219
self.assertRaises(OverflowError, getargs_l, LONG_MIN-1)
164220
self.assertEqual(LONG_MIN, getargs_l(LONG_MIN))
@@ -174,6 +230,10 @@ def test_n(self):
174230
# (PY_SSIZE_T_MIN ... PY_SSIZE_T_MAX)
175231
self.assertRaises(TypeError, getargs_n, 3.14)
176232
self.assertRaises(TypeError, getargs_n, Int())
233+
self.assertEqual(0, getargs_n(IntSubclass()))
234+
self.assertRaises(TypeError, getargs_n, BadInt())
235+
self.assertRaises(TypeError, getargs_n, BadInt2())
236+
self.assertEqual(0, getargs_n(BadInt3()))
177237

178238
self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MIN-1)
179239
self.assertEqual(PY_SSIZE_T_MIN, getargs_n(PY_SSIZE_T_MIN))
@@ -192,6 +252,11 @@ def test_L(self):
192252
self.assertRaises(TypeError, getargs_L, 3.14)
193253
self.assertRaises(TypeError, getargs_L, "Hello")
194254
self.assertEqual(99, getargs_L(Int()))
255+
self.assertEqual(0, getargs_L(IntSubclass()))
256+
self.assertRaises(TypeError, getargs_L, BadInt())
257+
with self.assertWarns(DeprecationWarning):
258+
self.assertEqual(1, getargs_L(BadInt2()))
259+
self.assertEqual(0, getargs_L(BadInt3()))
195260

196261
self.assertRaises(OverflowError, getargs_L, LLONG_MIN-1)
197262
self.assertEqual(LLONG_MIN, getargs_L(LLONG_MIN))
@@ -206,6 +271,11 @@ def test_K(self):
206271
# K return 'unsigned long long', no range checking
207272
self.assertRaises(TypeError, getargs_K, 3.14)
208273
self.assertRaises(TypeError, getargs_K, Int())
274+
self.assertEqual(0, getargs_K(IntSubclass()))
275+
self.assertRaises(TypeError, getargs_K, BadInt())
276+
self.assertRaises(TypeError, getargs_K, BadInt2())
277+
self.assertEqual(0, getargs_K(BadInt3()))
278+
209279
self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX))
210280
self.assertEqual(0, getargs_K(0))
211281
self.assertEqual(0, getargs_K(ULLONG_MAX+1))

Lib/test/test_index.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __index__(self):
99

1010
class TrapInt(int):
1111
def __index__(self):
12-
return self
12+
return int(self)
1313

1414
class BaseTestCase(unittest.TestCase):
1515
def setUp(self):
@@ -55,6 +55,40 @@ def test_error(self):
5555
self.assertRaises(TypeError, slice(self.o).indices, 0)
5656
self.assertRaises(TypeError, slice(self.n).indices, 0)
5757

58+
def test_int_subclass_with_index(self):
59+
# __index__ should be used when computing indices, even for int
60+
# subclasses. See issue #17576.
61+
class MyInt(int):
62+
def __index__(self):
63+
return int(self) + 1
64+
65+
my_int = MyInt(7)
66+
direct_index = my_int.__index__()
67+
operator_index = operator.index(my_int)
68+
self.assertEqual(direct_index, 8)
69+
self.assertEqual(operator_index, 7)
70+
# Both results should be of exact type int.
71+
self.assertIs(type(direct_index), int)
72+
#self.assertIs(type(operator_index), int)
73+
74+
def test_index_returns_int_subclass(self):
75+
class BadInt:
76+
def __index__(self):
77+
return True
78+
79+
class BadInt2(int):
80+
def __index__(self):
81+
return True
82+
83+
bad_int = BadInt()
84+
with self.assertWarns(DeprecationWarning):
85+
n = operator.index(bad_int)
86+
self.assertEqual(n, 1)
87+
88+
bad_int = BadInt2()
89+
n = operator.index(bad_int)
90+
self.assertEqual(n, 0)
91+
5892

5993
class SeqTestCase:
6094
# This test case isn't run directly. It just defines common tests

Lib/test/test_int.py

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -263,32 +263,7 @@ class Foo0:
263263
def __int__(self):
264264
return 42
265265

266-
class Foo1(object):
267-
def __int__(self):
268-
return 42
269-
270-
class Foo2(int):
271-
def __int__(self):
272-
return 42
273-
274-
class Foo3(int):
275-
def __int__(self):
276-
return self
277-
278-
class Foo4(int):
279-
def __int__(self):
280-
return 42
281-
282-
class Foo5(int):
283-
def __int__(self):
284-
return 42.
285-
286266
self.assertEqual(int(Foo0()), 42)
287-
self.assertEqual(int(Foo1()), 42)
288-
self.assertEqual(int(Foo2()), 42)
289-
self.assertEqual(int(Foo3()), 0)
290-
self.assertEqual(int(Foo4()), 42)
291-
self.assertRaises(TypeError, int, Foo5())
292267

293268
class Classic:
294269
pass
@@ -351,6 +326,57 @@ def __trunc__(self):
351326
with self.assertRaises(TypeError):
352327
int(TruncReturnsBadInt())
353328

329+
def test_int_subclass_with_int(self):
330+
class MyInt(int):
331+
def __int__(self):
332+
return 42
333+
334+
class BadInt(int):
335+
def __int__(self):
336+
return 42.0
337+
338+
my_int = MyInt(7)
339+
self.assertEqual(my_int, 7)
340+
self.assertEqual(int(my_int), 42)
341+
342+
self.assertRaises(TypeError, int, BadInt())
343+
344+
def test_int_returns_int_subclass(self):
345+
class BadInt:
346+
def __int__(self):
347+
return True
348+
349+
class BadInt2(int):
350+
def __int__(self):
351+
return True
352+
353+
class TruncReturnsBadInt:
354+
def __trunc__(self):
355+
return BadInt()
356+
357+
class TruncReturnsIntSubclass:
358+
def __trunc__(self):
359+
return True
360+
361+
bad_int = BadInt()
362+
with self.assertWarns(DeprecationWarning):
363+
n = int(bad_int)
364+
self.assertEqual(n, 1)
365+
366+
bad_int = BadInt2()
367+
with self.assertWarns(DeprecationWarning):
368+
n = int(bad_int)
369+
self.assertEqual(n, 1)
370+
371+
bad_int = TruncReturnsBadInt()
372+
with self.assertWarns(DeprecationWarning):
373+
n = int(bad_int)
374+
self.assertEqual(n, 1)
375+
376+
good_int = TruncReturnsIntSubclass()
377+
n = int(good_int)
378+
self.assertEqual(n, 1)
379+
354380
def test_error_message(self):
355381
def check(s, base=None):
356382
with self.assertRaises(ValueError,

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.3.4 release candidate 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #17576: Deprecation warning emitted now when __int__() or __index__()
14+
return not int instance.
15+
1316
- Issue #19932: Fix typo in import.h, missing whitespaces in function prototypes.
1417

1518
- Issue #19729: In str.format(), fix recursive expansion in format spec.

0 commit comments

Comments
 (0)