[Python-checkins] bpo-42222: Modernize integer test/conversion in randrange() (#23064)

rhettinger webhook-mailer at python.org
Mon Dec 28 14:10:54 EST 2020


https://github.com/python/cpython/commit/a9621bb301dba44494e81edc00e3a3b62c96af26
commit: a9621bb301dba44494e81edc00e3a3b62c96af26
branch: master
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: rhettinger <rhettinger at users.noreply.github.com>
date: 2020-12-28T11:10:34-08:00
summary:

bpo-42222: Modernize integer test/conversion in randrange() (#23064)

files:
A Misc/NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst
M Doc/library/random.rst
M Lib/random.py
M Lib/test/test_random.py

diff --git a/Doc/library/random.rst b/Doc/library/random.rst
index fa5c74b40b25e..07ee011521429 100644
--- a/Doc/library/random.rst
+++ b/Doc/library/random.rst
@@ -135,6 +135,15 @@ Functions for integers
       values.  Formerly it used a style like ``int(random()*n)`` which could produce
       slightly uneven distributions.
 
+   .. deprecated:: 3.10
+      The automatic conversion of non-integer types to equivalent integers is
+      deprecated.  Currently ``randrange(10.0)`` is losslessly converted to
+      ``randrange(10)``.  In the future, this will raise a :exc:`TypeError`.
+
+   .. deprecated:: 3.10
+      The exception raised for non-integral values such as ``range(10.5)``
+      will be changed from :exc:`ValueError` to :exc:`TypeError`.
+
 .. function:: randint(a, b)
 
    Return a random integer *N* such that ``a <= N <= b``.  Alias for
diff --git a/Lib/random.py b/Lib/random.py
index 66433baa8cc18..a4128c28fb2c6 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -51,6 +51,7 @@
 from math import tau as TWOPI, floor as _floor, isfinite as _isfinite
 from os import urandom as _urandom
 from _collections_abc import Set as _Set, Sequence as _Sequence
+from operator import index as _index
 from itertools import accumulate as _accumulate, repeat as _repeat
 from bisect import bisect as _bisect
 import os as _os
@@ -297,28 +298,59 @@ def randrange(self, start, stop=None, step=1):
 
         # This code is a bit messy to make it fast for the
         # common case while still doing adequate error checking.
-        istart = int(start)
-        if istart != start:
-            raise ValueError("non-integer arg 1 for randrange()")
+        try:
+            istart = _index(start)
+        except TypeError:
+            if int(start) == start:
+                istart = int(start)
+                _warn('Float arguments to randrange() have been deprecated\n'
+                      'since Python 3.10 and will be removed in a subsequent '
+                      'version.',
+                      DeprecationWarning, 2)
+            else:
+                _warn('randrange() will raise TypeError in the future',
+                      DeprecationWarning, 2)
+                raise ValueError("non-integer arg 1 for randrange()")
         if stop is None:
             if istart > 0:
                 return self._randbelow(istart)
             raise ValueError("empty range for randrange()")
 
         # stop argument supplied.
-        istop = int(stop)
-        if istop != stop:
-            raise ValueError("non-integer stop for randrange()")
+        try:
+            istop = _index(stop)
+        except TypeError:
+            if int(stop) == stop:
+                istop = int(stop)
+                _warn('Float arguments to randrange() have been deprecated\n'
+                      'since Python 3.10 and will be removed in a subsequent '
+                      'version.',
+                      DeprecationWarning, 2)
+            else:
+                _warn('randrange() will raise TypeError in the future',
+                      DeprecationWarning, 2)
+                raise ValueError("non-integer stop for randrange()")
+
+        try:
+            istep = _index(step)
+        except TypeError:
+            if int(step) == step:
+                istep = int(step)
+                _warn('Float arguments to randrange() have been deprecated\n'
+                      'since Python 3.10 and will be removed in a subsequent '
+                      'version.',
+                      DeprecationWarning, 2)
+            else:
+                _warn('randrange() will raise TypeError in the future',
+                      DeprecationWarning, 2)
+                raise ValueError("non-integer step for randrange()")
         width = istop - istart
-        if step == 1 and width > 0:
+        if istep == 1 and width > 0:
             return istart + self._randbelow(width)
-        if step == 1:
+        if istep == 1:
             raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
 
         # Non-unit step argument supplied.
-        istep = int(step)
-        if istep != step:
-            raise ValueError("non-integer step for randrange()")
         if istep > 0:
             n = (width + istep - 1) // istep
         elif istep < 0:
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index e7f911d12875e..436f3c98e6394 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -542,6 +542,26 @@ def test_randrange_errors(self):
         raises(0, 42, 0)
         raises(0, 42, 3.14159)
 
+    def test_randrange_argument_handling(self):
+        randrange = self.gen.randrange
+        with self.assertWarns(DeprecationWarning):
+            randrange(10.0, 20, 2)
+        with self.assertWarns(DeprecationWarning):
+            randrange(10, 20.0, 2)
+        with self.assertWarns(DeprecationWarning):
+            randrange(10, 20, 1.0)
+        with self.assertWarns(DeprecationWarning):
+            randrange(10, 20, 2.0)
+        with self.assertWarns(DeprecationWarning):
+            with self.assertRaises(ValueError):
+                randrange(10.5)
+        with self.assertWarns(DeprecationWarning):
+            with self.assertRaises(ValueError):
+                randrange(10, 20.5)
+        with self.assertWarns(DeprecationWarning):
+            with self.assertRaises(ValueError):
+                randrange(10, 20, 1.5)
+
     def test_randbelow_logic(self, _log=log, int=int):
         # check bitcount transition points:  2**i and 2**(i+1)-1
         # show that: k = int(1.001 + _log(n, 2))
diff --git a/Misc/NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst b/Misc/NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst
new file mode 100644
index 0000000000000..2f570bb9824d0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst
@@ -0,0 +1,9 @@
+Harmonized random.randrange() argument handling to match range().
+
+* The integer test and conversion in randrange() now uses
+  operator.index().
+* Non-integer arguments to randrange() are deprecated.
+* The *ValueError* is deprecated in favor of a *TypeError*.
+* It now runs a little faster than before.
+
+(Contributed by Raymond Hettinger and Serhiy Storchaka.)



More information about the Python-checkins mailing list