Skip to content

Commit 1b34d25

Browse files
committed
Issue #5080: turn the DeprecationWarning from float arguments passed
to integer PyArg_Parse* format codes into a TypeError. Add a DeprecationWarning for floats passed with the 'L' format code, which didn't previously have a warning.
1 parent edfe72f commit 1b34d25

6 files changed

Lines changed: 65 additions & 28 deletions

File tree

Lib/os.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ def urandom(n):
752752
raise NotImplementedError("/dev/urandom (or equivalent) not found")
753753
try:
754754
bs = b""
755-
while n - len(bs) >= 1:
755+
while n > len(bs):
756756
bs += read(_urandomfd, n - len(bs))
757757
finally:
758758
close(_urandomfd)

Lib/test/test_getargs2.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
import unittest
22
from test import test_support
33
from _testcapi import getargs_keywords
4-
54
import warnings
6-
warnings.filterwarnings("ignore",
7-
category=DeprecationWarning,
8-
message=".*integer argument expected, got float",
9-
module=__name__)
10-
warnings.filterwarnings("ignore",
11-
category=DeprecationWarning,
12-
message=".*integer argument expected, got float",
13-
module="unittest")
145

156
"""
167
> How about the following counterproposal. This also changes some of
@@ -68,7 +59,7 @@ class Unsigned_TestCase(unittest.TestCase):
6859
def test_b(self):
6960
from _testcapi import getargs_b
7061
# b returns 'unsigned char', and does range checking (0 ... UCHAR_MAX)
71-
self.assertEqual(3, getargs_b(3.14))
62+
self.assertRaises(TypeError, getargs_b, 3.14)
7263
self.assertEqual(99, getargs_b(Long()))
7364
self.assertEqual(99, getargs_b(Int()))
7465

@@ -84,7 +75,7 @@ def test_b(self):
8475
def test_B(self):
8576
from _testcapi import getargs_B
8677
# B returns 'unsigned char', no range checking
87-
self.assertEqual(3, getargs_B(3.14))
78+
self.assertRaises(TypeError, getargs_B, 3.14)
8879
self.assertEqual(99, getargs_B(Long()))
8980
self.assertEqual(99, getargs_B(Int()))
9081

@@ -101,7 +92,7 @@ def test_B(self):
10192
def test_H(self):
10293
from _testcapi import getargs_H
10394
# H returns 'unsigned short', no range checking
104-
self.assertEqual(3, getargs_H(3.14))
95+
self.assertRaises(TypeError, getargs_H, 3.14)
10596
self.assertEqual(99, getargs_H(Long()))
10697
self.assertEqual(99, getargs_H(Int()))
10798

@@ -118,7 +109,7 @@ def test_H(self):
118109
def test_I(self):
119110
from _testcapi import getargs_I
120111
# I returns 'unsigned int', no range checking
121-
self.assertEqual(3, getargs_I(3.14))
112+
self.assertRaises(TypeError, getargs_I, 3.14)
122113
self.assertEqual(99, getargs_I(Long()))
123114
self.assertEqual(99, getargs_I(Int()))
124115

@@ -154,7 +145,7 @@ class Signed_TestCase(unittest.TestCase):
154145
def test_h(self):
155146
from _testcapi import getargs_h
156147
# h returns 'short', and does range checking (SHRT_MIN ... SHRT_MAX)
157-
self.assertEqual(3, getargs_h(3.14))
148+
self.assertRaises(TypeError, getargs_h, 3.14)
158149
self.assertEqual(99, getargs_h(Long()))
159150
self.assertEqual(99, getargs_h(Int()))
160151

@@ -170,7 +161,7 @@ def test_h(self):
170161
def test_i(self):
171162
from _testcapi import getargs_i
172163
# i returns 'int', and does range checking (INT_MIN ... INT_MAX)
173-
self.assertEqual(3, getargs_i(3.14))
164+
self.assertRaises(TypeError, getargs_i, 3.14)
174165
self.assertEqual(99, getargs_i(Long()))
175166
self.assertEqual(99, getargs_i(Int()))
176167

@@ -186,7 +177,7 @@ def test_i(self):
186177
def test_l(self):
187178
from _testcapi import getargs_l
188179
# l returns 'long', and does range checking (LONG_MIN ... LONG_MAX)
189-
self.assertEqual(3, getargs_l(3.14))
180+
self.assertRaises(TypeError, getargs_l, 3.14)
190181
self.assertEqual(99, getargs_l(Long()))
191182
self.assertEqual(99, getargs_l(Int()))
192183

@@ -203,7 +194,7 @@ def test_n(self):
203194
from _testcapi import getargs_n
204195
# n returns 'Py_ssize_t', and does range checking
205196
# (PY_SSIZE_T_MIN ... PY_SSIZE_T_MAX)
206-
self.assertEqual(3, getargs_n(3.14))
197+
self.assertRaises(TypeError, getargs_n, 3.14)
207198
self.assertEqual(99, getargs_n(Long()))
208199
self.assertEqual(99, getargs_n(Int()))
209200

@@ -220,9 +211,24 @@ def test_n(self):
220211
class LongLong_TestCase(unittest.TestCase):
221212
def test_L(self):
222213
from _testcapi import getargs_L
223-
# L returns 'long long', and does range checking (LLONG_MIN ... LLONG_MAX)
214+
# L returns 'long long', and does range checking (LLONG_MIN
215+
# ... LLONG_MAX)
216+
with warnings.catch_warnings():
217+
warnings.filterwarnings(
218+
"ignore",
219+
category=DeprecationWarning,
220+
message=".*integer argument expected, got float",
221+
module=__name__)
222+
self.assertEqual(3, getargs_L(3.14))
223+
with warnings.catch_warnings():
224+
warnings.filterwarnings(
225+
"error",
226+
category=DeprecationWarning,
227+
message=".*integer argument expected, got float",
228+
module="unittest")
229+
self.assertRaises(DeprecationWarning, getargs_L, 3.14)
230+
224231
self.assertRaises(TypeError, getargs_L, "Hello")
225-
self.assertEqual(3, getargs_L(3.14))
226232
self.assertEqual(99, getargs_L(Long()))
227233
self.assertEqual(99, getargs_L(Int()))
228234

Lib/test/test_os.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -502,11 +502,9 @@ def test_urandom(self):
502502
self.assertEqual(len(os.urandom(100)), 100)
503503
self.assertEqual(len(os.urandom(1000)), 1000)
504504
# see http://bugs.python.org/issue3708
505-
with test_support.check_warnings():
506-
# silence deprecation warnings about float arguments
507-
self.assertEqual(len(os.urandom(0.9)), 0)
508-
self.assertEqual(len(os.urandom(1.1)), 1)
509-
self.assertEqual(len(os.urandom(2.0)), 2)
505+
self.assertRaises(TypeError, os.urandom, 0.9)
506+
self.assertRaises(TypeError, os.urandom, 1.1)
507+
self.assertRaises(TypeError, os.urandom, 2.0)
510508
except NotImplementedError:
511509
pass
512510

Lib/test/test_xrange.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def test_xrange(self):
8181
self.assertRaises(TypeError, xrange, 1, 2, 3, 4)
8282
self.assertRaises(ValueError, xrange, 1, 2, 0)
8383

84-
self.assertRaises(OverflowError, xrange, 1e100, 1e101, 1e101)
84+
self.assertRaises(OverflowError, xrange, 10**100, 10**101, 10**101)
8585

8686
self.assertRaises(TypeError, xrange, 0, "spam")
8787
self.assertRaises(TypeError, xrange, 0, 42, "spam")

Misc/NEWS

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ What's New in Python 2.7 alpha 2?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #5080: A number of functions and methods previously produced a
16+
DeprecationWarning when passed a float argument where an integer was
17+
expected. These functions and methods now raise TypeError instead.
18+
The majority of the effects of this change are in the extension
19+
modules, but some core functions and methods are affected: notably
20+
the 'chr', 'range' and 'xrange' builtins, and many unicode/str
21+
methods.
22+
1523
- Issue #7604: Deleting an unset slotted attribute did not raise an
1624
AttributeError.
1725

@@ -88,6 +96,14 @@ Library
8896
C-API
8997
-----
9098

99+
- Issue #5080: The argument parsing functions PyArg_ParseTuple,
100+
PyArg_ParseTupleAndKeywords, PyArg_VaParse,
101+
PyArg_VaParseTupleAndKeywords and PyArg_Parse no longer accept float
102+
arguments for integer format codes (other than 'L'): previously an
103+
attempt to pass a float resulted in a DeprecationWarning; now it
104+
gives a TypeError. For the 'L' format code (which previously had no
105+
warning) there is now a DeprecationWarning.
106+
91107
- Issue #7033: function ``PyErr_NewExceptionWithDoc()`` added.
92108

93109
Build

Python/getargs.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ converterr(const char *expected, PyObject *arg, char *msgbuf, size_t bufsize)
526526
/* explicitly check for float arguments when integers are expected. For now
527527
* signal a warning. Returns true if an exception was raised. */
528528
static int
529-
float_argument_error(PyObject *arg)
529+
float_argument_warning(PyObject *arg)
530530
{
531531
if (PyFloat_Check(arg) &&
532532
PyErr_Warn(PyExc_DeprecationWarning,
@@ -536,6 +536,20 @@ float_argument_error(PyObject *arg)
536536
return 0;
537537
}
538538

539+
/* explicitly check for float arguments when integers are expected. Raises
540+
TypeError and returns true for float arguments. */
541+
static int
542+
float_argument_error(PyObject *arg)
543+
{
544+
if (PyFloat_Check(arg)) {
545+
PyErr_SetString(PyExc_TypeError,
546+
"integer argument expected, got float");
547+
return 1;
548+
}
549+
else
550+
return 0;
551+
}
552+
539553
/* Convert a non-tuple argument. Return NULL if conversion went OK,
540554
or a string with a message describing the failure. The message is
541555
formatted as "must be <desired type>, not <actual type>".
@@ -719,7 +733,10 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
719733
#ifdef HAVE_LONG_LONG
720734
case 'L': {/* PY_LONG_LONG */
721735
PY_LONG_LONG *p = va_arg( *p_va, PY_LONG_LONG * );
722-
PY_LONG_LONG ival = PyLong_AsLongLong( arg );
736+
PY_LONG_LONG ival;
737+
if (float_argument_warning(arg))
738+
return converterr("long<L>", arg, msgbuf, bufsize);
739+
ival = PyLong_AsLongLong(arg);
723740
if (ival == (PY_LONG_LONG)-1 && PyErr_Occurred() ) {
724741
return converterr("long<L>", arg, msgbuf, bufsize);
725742
} else {

0 commit comments

Comments
 (0)