Skip to content

bpo-38881: choices() raises ValueError when all weights are zero #17362

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 3 commits into from
Nov 23, 2019
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
8 changes: 6 additions & 2 deletions Doc/library/random.rst
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,9 @@ Functions for sequences

The *weights* or *cum_weights* can use any numeric type that interoperates
with the :class:`float` values returned by :func:`random` (that includes
integers, floats, and fractions but excludes decimals). Weights are
assumed to be non-negative.
integers, floats, and fractions but excludes decimals). Behavior is
undefined if any weight is negative. A :exc:`ValueError` is raised if all
weights are zero.

For a given seed, the :func:`choices` function with equal weighting
typically produces a different sequence than repeated calls to
Expand All @@ -177,6 +178,9 @@ Functions for sequences

.. versionadded:: 3.6

.. versionchanged:: 3.9
Raises a :exc:`ValueError` if all weights are zero.


.. function:: shuffle(x[, random])

Expand Down
4 changes: 3 additions & 1 deletion Lib/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,10 @@ def choices(self, population, weights=None, *, cum_weights=None, k=1):
raise TypeError('Cannot specify both weights and cumulative weights')
if len(cum_weights) != n:
raise ValueError('The number of weights does not match the population')
bisect = _bisect
total = cum_weights[-1] + 0.0 # convert to float
if total <= 0.0:
raise ValueError('Total of weights must be greater than zero')
bisect = _bisect
hi = n - 1
return [population[bisect(cum_weights, random() * total, 0, hi)]
for i in _repeat(None, k)]
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ def test_choices_subnormal(self):
choices = self.gen.choices
choices(population=[1, 2], weights=[1e-323, 1e-323], k=5000)

def test_choices_with_all_zero_weights(self):
# See issue #38881
with self.assertRaises(ValueError):
self.gen.choices('AB', [0.0, 0.0])

def test_gauss(self):
# Ensure that the seed() method initializes all the hidden state. In
# particular, through 2.2.1 it failed to reset a piece of state used
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
random.choices() now raises a ValueError when all the weights are zero.