Skip to content
Merged
Prev Previous commit
Next Next commit
Merge branch 'master' into randrange-index
  • Loading branch information
serhiy-storchaka committed Oct 31, 2020
commit fb9382e66d82d5c4986d909f1ec9382547afcec1
2 changes: 1 addition & 1 deletion Doc/library/random.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Functions for integers
values. Formerly it used a style like ``int(random()*n)`` which could produce
slightly uneven distributions.

.. deprecated:: 3.9
.. deprecated:: 3.10
Accepting non-integer arguments is deprecated.


Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ Deprecated
as appropriate to help identify code which needs updating during
this transition.

* Deprecated support of non-integer arguments in :func:`random.randrange`.
(Contributed by Serhiy Storchaka in :issue:`37319`.)


Removed
=======
Expand Down
22 changes: 20 additions & 2 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -906,9 +906,27 @@ Deprecated
by :c:func:`Py_Initialize()` since Python 3.7.
(Contributed by Victor Stinner in :issue:`39877`.)

* Deprecated support of non-integer arguments in :func:`random.randrange`.
(Contributed by Serhiy Storchaka in :issue:`37319`.)
* Passing ``None`` as the first argument to the :func:`shlex.split` function
has been deprecated. (Contributed by Zackery Spytz in :issue:`33262`.)

* :func:`smtpd.MailmanProxy` is now deprecated as it is unusable without
an external module, ``mailman``. (Contributed by Samuel Colvin in :issue:`35800`.)

* The :mod:`lib2to3` module now emits a :exc:`PendingDeprecationWarning`.
Python 3.9 switched to a PEG parser (see :pep:`617`), and Python 3.10 may
include new language syntax that is not parsable by lib2to3's LL(1) parser.
The ``lib2to3`` module may be removed from the standard library in a future
Python version. Consider third-party alternatives such as `LibCST`_ or
`parso`_.
(Contributed by Carl Meyer in :issue:`40360`.)

* The *random* parameter of :func:`random.shuffle` has been deprecated.
(Contributed by Raymond Hettinger in :issue:`40465`)

.. _LibCST: https://libcst.readthedocs.io/
.. _parso: https://parso.readthedocs.io/

.. _removed-in-python-39:

Removed
=======
Expand Down
86 changes: 81 additions & 5 deletions Lib/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@
LOG4 = _log(4.0)
SG_MAGICCONST = 1.0 + _log(4.5)
BPF = 53 # Number of bits in a float
RECIP_BPF = 2**-BPF
RECIP_BPF = 2 ** -BPF
_ONE = 1
Comment thread
serhiy-storchaka marked this conversation as resolved.


class Random(_random.Random):
"""Random number generator base class used by bound module functions.

Expand Down Expand Up @@ -213,7 +214,82 @@ def __reduce__(self):
return self.__class__, (), self.getstate()


def randrange(self, start, stop=None, step=_ONE, _index=_index, _int=int):
## ---- internal support method for evenly distributed integers ----

def __init_subclass__(cls, /, **kwargs):
"""Control how subclasses generate random integers.

The algorithm a subclass can use depends on the random() and/or
getrandbits() implementation available to it and determines
whether it can generate random integers from arbitrarily large
ranges.
"""

for c in cls.__mro__:
if '_randbelow' in c.__dict__:
# just inherit it
break
if 'getrandbits' in c.__dict__:
cls._randbelow = cls._randbelow_with_getrandbits
break
if 'random' in c.__dict__:
cls._randbelow = cls._randbelow_without_getrandbits
break

def _randbelow_with_getrandbits(self, n):
"Return a random int in the range [0,n). Returns 0 if n==0."

if not n:
return 0
getrandbits = self.getrandbits
k = n.bit_length() # don't use (n-1) here because n can be 1
r = getrandbits(k) # 0 <= r < 2**k
while r >= n:
r = getrandbits(k)
return r

def _randbelow_without_getrandbits(self, n, maxsize=1<<BPF):
"""Return a random int in the range [0,n). Returns 0 if n==0.

The implementation does not use getrandbits, but only random.
"""

random = self.random
if n >= maxsize:
_warn("Underlying random() generator does not supply \n"
"enough bits to choose from a population range this large.\n"
"To remove the range limitation, add a getrandbits() method.")
return _floor(random() * n)
if n == 0:
return 0
rem = maxsize % n
limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0
r = random()
while r >= limit:
r = random()
return _floor(r * maxsize) % n

_randbelow = _randbelow_with_getrandbits


## --------------------------------------------------------
## ---- Methods below this point generate custom distributions
## ---- based on the methods defined above. They do not
## ---- directly touch the underlying generator and only
## ---- access randomness through the methods: random(),
## ---- getrandbits(), or _randbelow().


## -------------------- bytes methods ---------------------

def randbytes(self, n):
"""Generate n random bytes."""
return self.getrandbits(n * 8).to_bytes(n, 'little')


## -------------------- integer methods -------------------

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

This fixes the problem with randint() which includes the
Expand All @@ -226,7 +302,7 @@ def randrange(self, start, stop=None, step=_ONE, _index=_index, _int=int):
try:
istart = _index(start)
except TypeError:
istart = _int(start)
istart = int(start)
if istart != start:
raise ValueError("non-integer arg 1 for randrange()")
_warn('non-integer arg 1 for randrange()',
Expand All @@ -240,7 +316,7 @@ def randrange(self, start, stop=None, step=_ONE, _index=_index, _int=int):
try:
istop = _index(stop)
except TypeError:
istop = _int(stop)
istop = int(stop)
if istop != stop:
raise ValueError("non-integer stop for randrange()")
_warn('non-integer stop for randrange()',
Expand All @@ -256,7 +332,7 @@ def randrange(self, start, stop=None, step=_ONE, _index=_index, _int=int):
try:
istep = _index(step)
except TypeError:
istep = _int(step)
istep = int(step)
if istep != step:
raise ValueError("non-integer step for randrange()")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're at it, the exception type should be converted to TypeError.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we raise it with custom message or reraise the exception raised by index()?

_warn('non-integer step for randrange()',
Expand Down
You are viewing a condensed version of this merge commit. You can view the full changes here.