Skip to content

bpo-42222: Remove deprecated support for non-integer values #28983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions Doc/library/random.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,10 @@ 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 ``randrange(10.5)``
or ``randrange('10')`` will be changed from :exc:`ValueError` to
:exc:`TypeError`.
.. versionchanged:: 3.11
Automatic conversion of non-integer types is no longer supported.
Calls such as ``randrange(10.0)`` and ``randrange(Fraction(10, 1))``
now raise a :exc:`TypeError`.

.. function:: randint(a, b)

Expand Down
52 changes: 10 additions & 42 deletions Lib/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,27 +286,17 @@ def randbytes(self, n):
## -------------------- integer methods -------------------

def randrange(self, start, stop=None, step=_ONE):
"""Choose a random item from range(start, stop[, step]).
"""Choose a random item from range(stop) or range(start, stop[, step]).

This fixes the problem with randint() which includes the
endpoint; in Python this is usually not what you want.
Roughly equivalent to ``choice(range(start, stop, step))``
but supports arbitrarily large ranges and is optimized
for common cases.

"""

# This code is a bit messy to make it fast for the
# common case while still doing adequate error checking.
try:
istart = _index(start)
except TypeError:
istart = int(start)
if istart != start:
_warn('randrange() will raise TypeError in the future',
DeprecationWarning, 2)
raise ValueError("non-integer arg 1 for randrange()")
_warn('non-integer arguments to randrange() have been deprecated '
'since Python 3.10 and will be removed in a subsequent '
'version',
DeprecationWarning, 2)
istart = _index(start)
if stop is None:
# We don't check for "step != 1" because it hasn't been
# type checked and converted to an integer yet.
Expand All @@ -316,37 +306,15 @@ def randrange(self, start, stop=None, step=_ONE):
return self._randbelow(istart)
raise ValueError("empty range for randrange()")

# stop argument supplied.
try:
istop = _index(stop)
except TypeError:
istop = int(stop)
if istop != stop:
_warn('randrange() will raise TypeError in the future',
DeprecationWarning, 2)
raise ValueError("non-integer stop for randrange()")
_warn('non-integer arguments to randrange() have been deprecated '
'since Python 3.10 and will be removed in a subsequent '
'version',
DeprecationWarning, 2)
# Stop argument supplied.
istop = _index(stop)
width = istop - istart
try:
istep = _index(step)
except TypeError:
istep = int(step)
if istep != step:
_warn('randrange() will raise TypeError in the future',
DeprecationWarning, 2)
raise ValueError("non-integer step for randrange()")
_warn('non-integer arguments to randrange() have been deprecated '
'since Python 3.10 and will be removed in a subsequent '
'version',
DeprecationWarning, 2)
istep = _index(step)
# Fast path.
if istep == 1:
if width > 0:
return istart + self._randbelow(width)
raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
raise ValueError(f"empty range in randrange({start}, {stop}, {step})")

# Non-unit step argument supplied.
if istep > 0:
Expand All @@ -356,7 +324,7 @@ def randrange(self, start, stop=None, step=_ONE):
else:
raise ValueError("zero step for randrange()")
if n <= 0:
raise ValueError("empty range for randrange()")
raise ValueError(f"empty range in randrange({start}, {stop}, {step})")
return istart + istep * self._randbelow(n)

def randint(self, a, b):
Expand Down
75 changes: 39 additions & 36 deletions Lib/test/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,50 +481,53 @@ def test_randrange_nonunit_step(self):
self.assertEqual(rint, 0)

def test_randrange_errors(self):
raises = partial(self.assertRaises, ValueError, self.gen.randrange)
raises_value_error = partial(self.assertRaises, ValueError, self.gen.randrange)
raises_type_error = partial(self.assertRaises, TypeError, self.gen.randrange)

# Empty range
raises(3, 3)
raises(-721)
raises(0, 100, -12)
# Non-integer start/stop
self.assertWarns(DeprecationWarning, raises, 3.14159)
self.assertWarns(DeprecationWarning, self.gen.randrange, 3.0)
self.assertWarns(DeprecationWarning, self.gen.randrange, Fraction(3, 1))
self.assertWarns(DeprecationWarning, raises, '3')
self.assertWarns(DeprecationWarning, raises, 0, 2.71828)
self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 2.0)
self.assertWarns(DeprecationWarning, self.gen.randrange, 0, Fraction(2, 1))
self.assertWarns(DeprecationWarning, raises, 0, '2')
# Zero and non-integer step
raises(0, 42, 0)
self.assertWarns(DeprecationWarning, raises, 0, 42, 0.0)
self.assertWarns(DeprecationWarning, raises, 0, 0, 0.0)
self.assertWarns(DeprecationWarning, raises, 0, 42, 3.14159)
self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, 3.0)
self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, Fraction(3, 1))
self.assertWarns(DeprecationWarning, raises, 0, 42, '3')
self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, 1.0)
self.assertWarns(DeprecationWarning, raises, 0, 0, 1.0)
raises_value_error(3, 3)
raises_value_error(-721)
raises_value_error(0, 100, -12)

# Zero step
raises_value_error(0, 42, 0)

# Non-integer start/stop/step
raises_type_error(3.14159)
raises_type_error(3.0)
raises_type_error(Fraction(3, 1))
raises_type_error('3')
raises_type_error(0, 2.71827)
raises_type_error(0, 2.0)
raises_type_error(0, Fraction(2, 1))
raises_type_error(0, '2')

# Non-integer step
raises_type_error(0, 42, 1.0)
raises_type_error(0, 0, 1.0)
raises_type_error(0, 42, 3.14159)
raises_type_error(0, 42, 3.0)
raises_type_error(0, 42, Fraction(3, 1))
raises_type_error(0, 42, '3')
raises_type_error(0, 42, 1.0)
raises_type_error(0, 0, 1.0)

def test_randrange_argument_handling(self):
randrange = self.gen.randrange
with self.assertWarns(DeprecationWarning):
with self.assertRaises(TypeError):
randrange(10.0, 20, 2)
with self.assertWarns(DeprecationWarning):
with self.assertRaises(TypeError):
randrange(10, 20.0, 2)
with self.assertWarns(DeprecationWarning):
with self.assertRaises(TypeError):
randrange(10, 20, 1.0)
with self.assertWarns(DeprecationWarning):
with self.assertRaises(TypeError):
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)
with self.assertRaises(TypeError):
randrange(10.5)
with self.assertRaises(TypeError):
randrange(10, 20.5)
with self.assertRaises(TypeError):
randrange(10, 20, 1.5)

def test_randrange_step(self):
# bpo-42772: When stop is None, the step argument was being ignored.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed deprecated support for float arguments in *randrange()*.