https://github.com/python/cpython/commit/8db2fa25753c98cde05525406036446770bb2c9c
commit: 8db2fa25753c98cde05525406036446770bb2c9c
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: rhettinger <[email protected]>
date: 2025-02-21T17:48:46Z
summary:
[3.12] gh-130285: Fix handling of zero or empty counts in random.sample()
(gh-130291) (gh-130417)
files:
A Misc/NEWS.d/next/Library/2025-02-21-10-32-05.gh-issue-130285.C0fkh7.rst
M Lib/random.py
M Lib/test/test_random.py
diff --git a/Lib/random.py b/Lib/random.py
index 1cfc2ba2f025b5..82eafe205280e0 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -417,11 +417,11 @@ def sample(self, population, k, *, counts=None):
cum_counts = list(_accumulate(counts))
if len(cum_counts) != n:
raise ValueError('The number of counts does not match the
population')
- total = cum_counts.pop()
+ total = cum_counts.pop() if cum_counts else 0
if not isinstance(total, int):
raise TypeError('Counts must be integers')
- if total <= 0:
- raise ValueError('Total of counts must be greater than zero')
+ if total < 0:
+ raise ValueError('Counts must be non-negative')
selections = self.sample(range(total), k=k)
bisect = _bisect
return [population[bisect(cum_counts, s)] for s in selections]
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index 50bea7be6d54c7..9a234a08389cfb 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -224,8 +224,6 @@ def test_sample_with_counts(self):
sample(['red', 'green', 'blue'], counts=10, k=10) #
counts not iterable
with self.assertRaises(ValueError):
sample(['red', 'green', 'blue'], counts=[-3, -7, -8], k=2) #
counts are negative
- with self.assertRaises(ValueError):
- sample(['red', 'green', 'blue'], counts=[0, 0, 0], k=2) #
counts are zero
with self.assertRaises(ValueError):
sample(['red', 'green'], counts=[10, 10], k=21) #
population too small
with self.assertRaises(ValueError):
@@ -233,6 +231,20 @@ def test_sample_with_counts(self):
with self.assertRaises(ValueError):
sample(['red', 'green', 'blue'], counts=[1, 2, 3, 4], k=2) #
too many counts
+ # Cases with zero counts match equivalents without counts (see
gh-130285)
+ self.assertEqual(
+ sample('abc', k=0, counts=[0, 0, 0]),
+ sample([], k=0),
+ )
+ self.assertEqual(
+ sample([], 0, counts=[]),
+ sample([], 0),
+ )
+ with self.assertRaises(ValueError):
+ sample([], 1, counts=[])
+ with self.assertRaises(ValueError):
+ sample('x', 1, counts=[0])
+
def test_choices(self):
choices = self.gen.choices
data = ['red', 'green', 'blue', 'yellow']
diff --git
a/Misc/NEWS.d/next/Library/2025-02-21-10-32-05.gh-issue-130285.C0fkh7.rst
b/Misc/NEWS.d/next/Library/2025-02-21-10-32-05.gh-issue-130285.C0fkh7.rst
new file mode 100644
index 00000000000000..7e0a4d219e385b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-02-21-10-32-05.gh-issue-130285.C0fkh7.rst
@@ -0,0 +1,4 @@
+Fix corner case for :func:`random.sample` allowing the *counts* parameter to
+specify an empty population. So now, ``sample([], 0, counts=[])`` and
+``sample('abc', k=0, counts=[0, 0, 0])`` both give the same result as
+``sample([], 0)``.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]