mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	bpo-42222: Modernize integer test/conversion in randrange() (#23064)
This commit is contained in:
		
							parent
							
								
									1031f23fc3
								
							
						
					
					
						commit
						a9621bb301
					
				
					 4 changed files with 81 additions and 11 deletions
				
			
		| 
						 | 
					@ -135,6 +135,15 @@ Functions for integers
 | 
				
			||||||
      values.  Formerly it used a style like ``int(random()*n)`` which could produce
 | 
					      values.  Formerly it used a style like ``int(random()*n)`` which could produce
 | 
				
			||||||
      slightly uneven distributions.
 | 
					      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)
 | 
					.. function:: randint(a, b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   Return a random integer *N* such that ``a <= N <= b``.  Alias for
 | 
					   Return a random integer *N* such that ``a <= N <= b``.  Alias for
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@
 | 
				
			||||||
from math import tau as TWOPI, floor as _floor, isfinite as _isfinite
 | 
					from math import tau as TWOPI, floor as _floor, isfinite as _isfinite
 | 
				
			||||||
from os import urandom as _urandom
 | 
					from os import urandom as _urandom
 | 
				
			||||||
from _collections_abc import Set as _Set, Sequence as _Sequence
 | 
					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 itertools import accumulate as _accumulate, repeat as _repeat
 | 
				
			||||||
from bisect import bisect as _bisect
 | 
					from bisect import bisect as _bisect
 | 
				
			||||||
import os as _os
 | 
					import os as _os
 | 
				
			||||||
| 
						 | 
					@ -297,8 +298,18 @@ def randrange(self, start, stop=None, step=1):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # This code is a bit messy to make it fast for the
 | 
					        # This code is a bit messy to make it fast for the
 | 
				
			||||||
        # common case while still doing adequate error checking.
 | 
					        # common case while still doing adequate error checking.
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            istart = _index(start)
 | 
				
			||||||
 | 
					        except TypeError:
 | 
				
			||||||
 | 
					            if int(start) == start:
 | 
				
			||||||
                istart = int(start)
 | 
					                istart = int(start)
 | 
				
			||||||
        if istart != 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()")
 | 
					                raise ValueError("non-integer arg 1 for randrange()")
 | 
				
			||||||
        if stop is None:
 | 
					        if stop is None:
 | 
				
			||||||
            if istart > 0:
 | 
					            if istart > 0:
 | 
				
			||||||
| 
						 | 
					@ -306,19 +317,40 @@ def randrange(self, start, stop=None, step=1):
 | 
				
			||||||
            raise ValueError("empty range for randrange()")
 | 
					            raise ValueError("empty range for randrange()")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # stop argument supplied.
 | 
					        # stop argument supplied.
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            istop = _index(stop)
 | 
				
			||||||
 | 
					        except TypeError:
 | 
				
			||||||
 | 
					            if int(stop) == stop:
 | 
				
			||||||
                istop = int(stop)
 | 
					                istop = int(stop)
 | 
				
			||||||
        if istop != 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()")
 | 
					                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
 | 
					        width = istop - istart
 | 
				
			||||||
        if step == 1 and width > 0:
 | 
					        if istep == 1 and width > 0:
 | 
				
			||||||
            return istart + self._randbelow(width)
 | 
					            return istart + self._randbelow(width)
 | 
				
			||||||
        if step == 1:
 | 
					        if istep == 1:
 | 
				
			||||||
            raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
 | 
					            raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Non-unit step argument supplied.
 | 
					        # Non-unit step argument supplied.
 | 
				
			||||||
        istep = int(step)
 | 
					 | 
				
			||||||
        if istep != step:
 | 
					 | 
				
			||||||
            raise ValueError("non-integer step for randrange()")
 | 
					 | 
				
			||||||
        if istep > 0:
 | 
					        if istep > 0:
 | 
				
			||||||
            n = (width + istep - 1) // istep
 | 
					            n = (width + istep - 1) // istep
 | 
				
			||||||
        elif istep < 0:
 | 
					        elif istep < 0:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -542,6 +542,26 @@ def test_randrange_errors(self):
 | 
				
			||||||
        raises(0, 42, 0)
 | 
					        raises(0, 42, 0)
 | 
				
			||||||
        raises(0, 42, 3.14159)
 | 
					        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):
 | 
					    def test_randbelow_logic(self, _log=log, int=int):
 | 
				
			||||||
        # check bitcount transition points:  2**i and 2**(i+1)-1
 | 
					        # check bitcount transition points:  2**i and 2**(i+1)-1
 | 
				
			||||||
        # show that: k = int(1.001 + _log(n, 2))
 | 
					        # show that: k = int(1.001 + _log(n, 2))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.)
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue