From e11b1dc05e664c236d15bcb801e37c08a1ff807d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 31 Oct 2020 10:12:05 -0700 Subject: [PATCH 1/9] Use index() instead of testing int(x)==x --- Lib/random.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index 139e8a40bb2724..4545240bd78fa1 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,8 +298,9 @@ 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: + try: + istart = _index(start) + except TypeError: raise ValueError("non-integer arg 1 for randrange()") if stop is None: if istart > 0: @@ -306,8 +308,9 @@ def randrange(self, start, stop=None, step=1): raise ValueError("empty range for randrange()") # stop argument supplied. - istop = int(stop) - if istop != stop: + try: + istop = _index(stop) + except TypeError: raise ValueError("non-integer stop for randrange()") width = istop - istart if step == 1 and width > 0: @@ -316,8 +319,9 @@ def randrange(self, start, stop=None, step=1): raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) # Non-unit step argument supplied. - istep = int(step) - if istep != step: + try: + istep = _index(step) + except TypeError: raise ValueError("non-integer step for randrange()") if istep > 0: n = (width + istep - 1) // istep From ebe8391f21853b6cd9cefdff6aed160e2e41101c Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 31 Oct 2020 10:28:49 -0700 Subject: [PATCH 2/9] Add blurb --- .../NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst 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 00000000000000..04978989da7790 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst @@ -0,0 +1 @@ +Modernized integer test/conversion in randrange() to use operator.index(). From ed2f6fdecb72405550b7e3a8ae104231c5818350 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 31 Oct 2020 11:09:04 -0700 Subject: [PATCH 3/9] Deprecate float arguments and the ValueError --- Lib/random.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index 4545240bd78fa1..8d0c752bfe8099 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -301,7 +301,16 @@ def randrange(self, start, stop=None, step=1): try: istart = _index(start) except TypeError: - raise ValueError("non-integer arg 1 for randrange()") + 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) @@ -311,7 +320,16 @@ def randrange(self, start, stop=None, step=1): try: istop = _index(stop) except TypeError: - raise ValueError("non-integer stop for randrange()") + 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()") width = istop - istart if step == 1 and width > 0: return istart + self._randbelow(width) @@ -322,7 +340,16 @@ def randrange(self, start, stop=None, step=1): try: istep = _index(step) except TypeError: - raise ValueError("non-integer step for randrange()") + 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()") if istep > 0: n = (width + istep - 1) // istep elif istep < 0: From ab9aeb75d07e8bd2bfff19a163f2b5f0ffd28296 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Dec 2020 00:32:42 -0800 Subject: [PATCH 4/9] Add more detail to blurb --- .../Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 index 04978989da7790..2598611e51e1d9 100644 --- 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 @@ -1 +1,9 @@ -Modernized integer test/conversion in randrange() to use operator.index(). +Harmonized random.randrange() argument handling to better match +that used by range(). + +* The integer test/conversion in randrange() now uses operator.index(). +* Float arguments to randrange() is 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.) From f32d175ea4ea487ecca725db239a432d9f688774 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Dec 2020 00:51:40 -0800 Subject: [PATCH 5/9] Add tests --- Lib/test/test_random.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 0c1fdeec9915ee..eed8f47d28bc35 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -517,6 +517,24 @@ 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, 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)) From ebcb2576e463dda6b99013310af93bf7f3039272 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Dec 2020 01:15:19 -0800 Subject: [PATCH 6/9] Update the docs --- Doc/library/random.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/library/random.rst b/Doc/library/random.rst index c243aced986e56..85a40d801fe0b3 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 From 832d885abcbd3e20c1969a07b5e68afa339a514e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Dec 2020 01:18:12 -0800 Subject: [PATCH 7/9] Improve blurb --- .../next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 2598611e51e1d9..d4f4d59f69d5ef 100644 --- 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 @@ -2,7 +2,7 @@ Harmonized random.randrange() argument handling to better match that used by range(). * The integer test/conversion in randrange() now uses operator.index(). -* Float arguments to randrange() is deprecated. +* Non-integer arguments to randrange() are deprecated. * The *ValueError* is deprecated in favor of a *TypeError*. * It now runs a little faster than before. From 9e6945d9d2a361c3ce947490823fab114ce96b81 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Dec 2020 14:20:22 -0800 Subject: [PATCH 8/9] Cover the case with a unit step --- Lib/random.py | 13 +++++++------ Lib/test/test_random.py | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index 8d0c752bfe8099..ffb60577bd6fde 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -330,13 +330,7 @@ def randrange(self, start, stop=None, step=1): _warn('randrange() will raise TypeError in the future', DeprecationWarning, 2) raise ValueError("non-integer stop for randrange()") - width = istop - istart - if step == 1 and width > 0: - return istart + self._randbelow(width) - if step == 1: - raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) - # Non-unit step argument supplied. try: istep = _index(step) except TypeError: @@ -350,6 +344,13 @@ def randrange(self, start, stop=None, step=1): _warn('randrange() will raise TypeError in the future', DeprecationWarning, 2) raise ValueError("non-integer step for randrange()") + width = istop - istart + if istep == 1 and width > 0: + return istart + self._randbelow(width) + if istep == 1: + raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) + + # Non-unit step argument supplied. 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 eed8f47d28bc35..6bc90ccbb3ba0a 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -523,6 +523,8 @@ def test_randrange_argument_handling(self): 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): From 85cc9ee2cc9edf476b5ebf1c2832151389b3a1bd Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Dec 2020 16:27:34 -0800 Subject: [PATCH 9/9] More concise blurb --- .../next/Library/2020-10-31-10-28-32.bpo-42222.Cfl1eR.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 index d4f4d59f69d5ef..2f570bb9824d08 100644 --- 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 @@ -1,7 +1,7 @@ -Harmonized random.randrange() argument handling to better match -that used by range(). +Harmonized random.randrange() argument handling to match range(). -* The integer test/conversion in randrange() now uses operator.index(). +* 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.