https://github.com/python/cpython/commit/3e1b8d6e678ed0d834c9bc37bbb599cd63e88a82
commit: 3e1b8d6e678ed0d834c9bc37bbb599cd63e88a82
branch: 3.13
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-06-02T23:16:30+03:00
summary:
[3.13] gh-133489: Remove size restrictions on getrandbits() and randbytes()
(GH-133658) (GH-134965)
random.getrandbits() can now generate more that 2**31 bits.
random.randbytes() can now generate more that 256 MiB.
(cherry picked from commit 68784fed78aa297f0de0d038742495709185bef5)
files:
A Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst
M Lib/test/test_random.py
M Modules/_randommodule.c
M Modules/clinic/_randommodule.c.h
M Objects/longobject.c
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index 96f6cc86219a5d..22b097b974c640 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -392,6 +392,8 @@ def test_getrandbits(self):
self.assertRaises(TypeError, self.gen.getrandbits)
self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
self.assertRaises(ValueError, self.gen.getrandbits, -1)
+ self.assertRaises(OverflowError, self.gen.getrandbits, 1<<1000)
+ self.assertRaises(ValueError, self.gen.getrandbits, -1<<1000)
self.assertRaises(TypeError, self.gen.getrandbits, 10.1)
def test_pickling(self):
@@ -435,6 +437,8 @@ def test_randbytes(self):
self.assertRaises(TypeError, self.gen.randbytes)
self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
self.assertRaises(ValueError, self.gen.randbytes, -1)
+ self.assertRaises(OverflowError, self.gen.randbytes, 1<<1000)
+ self.assertRaises((ValueError, OverflowError), self.gen.randbytes,
-1<<1000)
self.assertRaises(TypeError, self.gen.randbytes, 1.0)
def test_mu_sigma_default_args(self):
@@ -806,6 +810,22 @@ def test_getrandbits(self):
self.assertEqual(self.gen.getrandbits(100),
97904845777343510404718956115)
+ def test_getrandbits_2G_bits(self):
+ size = 2**31
+ self.gen.seed(1234567)
+ x = self.gen.getrandbits(size)
+ self.assertEqual(x.bit_length(), size)
+ self.assertEqual(x & (2**100-1), 890186470919986886340158459475)
+ self.assertEqual(x >> (size-100), 1226514312032729439655761284440)
+
+ @support.bigmemtest(size=2**32, memuse=1/8+2/15, dry_run=False)
+ def test_getrandbits_4G_bits(self, size):
+ self.gen.seed(1234568)
+ x = self.gen.getrandbits(size)
+ self.assertEqual(x.bit_length(), size)
+ self.assertEqual(x & (2**100-1), 287241425661104632871036099814)
+ self.assertEqual(x >> (size-100), 739728759900339699429794460738)
+
def test_randrange_uses_getrandbits(self):
# Verify use of getrandbits by randrange
# Use same seed as in the cross-platform repeatability test
@@ -962,6 +982,14 @@ def test_randbytes_getrandbits(self):
self.assertEqual(self.gen.randbytes(n),
gen2.getrandbits(n * 8).to_bytes(n, 'little'))
+ @support.bigmemtest(size=2**29, memuse=1+16/15, dry_run=False)
+ def test_randbytes_256M(self, size):
+ self.gen.seed(2849427419)
+ x = self.gen.randbytes(size)
+ self.assertEqual(len(x), size)
+ self.assertEqual(x[:12].hex(), 'f6fd9ae63855ab91ea238b4f')
+ self.assertEqual(x[-12:].hex(), '0e7af69a84ee99bf4a11becc')
+
def test_sample_counts_equivalence(self):
# Test the documented strong equivalence to a sample with repeated
elements.
# We run this test on random.Random() which makes deterministic
selections
diff --git
a/Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst
b/Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst
new file mode 100644
index 00000000000000..0c07beb76938f0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst
@@ -0,0 +1,2 @@
+:func:`random.getrandbits` can now generate more that 2\ :sup:`31` bits.
+:func:`random.randbytes` can now generate more that 256 MiB.
diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c
index 140640ae8fbf3a..8fb040fb6c5b35 100644
--- a/Modules/_randommodule.c
+++ b/Modules/_randommodule.c
@@ -495,34 +495,32 @@ _random_Random_setstate_impl(RandomObject *self, PyObject
*state)
_random.Random.getrandbits
self: self(type="RandomObject *")
- k: int
+ k: unsigned_long_long(bitwise=False)
/
getrandbits(k) -> x. Generates an int with k random bits.
[clinic start generated code]*/
static PyObject *
-_random_Random_getrandbits_impl(RandomObject *self, int k)
-/*[clinic end generated code: output=b402f82a2158887f input=87603cd60f79f730]*/
+_random_Random_getrandbits_impl(RandomObject *self, unsigned long long k)
+/*[clinic end generated code: output=25a604fab95885d4 input=88e51091eea2f042]*/
{
- int i, words;
+ Py_ssize_t i, words;
uint32_t r;
uint32_t *wordarray;
PyObject *result;
- if (k < 0) {
- PyErr_SetString(PyExc_ValueError,
- "number of bits must be non-negative");
- return NULL;
- }
-
if (k == 0)
return PyLong_FromLong(0);
if (k <= 32) /* Fast path */
return PyLong_FromUnsignedLong(genrand_uint32(self) >> (32 - k));
- words = (k - 1) / 32 + 1;
+ if ((k - 1u) / 32u + 1u > PY_SSIZE_T_MAX / 4u) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ words = (k - 1u) / 32u + 1u;
wordarray = (uint32_t *)PyMem_Malloc(words * 4);
if (wordarray == NULL) {
PyErr_NoMemory();
diff --git a/Modules/clinic/_randommodule.c.h b/Modules/clinic/_randommodule.c.h
index 6193acac67e7ac..12c845d4c44d00 100644
--- a/Modules/clinic/_randommodule.c.h
+++ b/Modules/clinic/_randommodule.c.h
@@ -3,6 +3,7 @@ preserve
[clinic start generated code]*/
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_long.h" // _PyLong_UnsignedLongLong_Converter()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(_random_Random_random__doc__,
@@ -124,16 +125,15 @@ PyDoc_STRVAR(_random_Random_getrandbits__doc__,
{"getrandbits", (PyCFunction)_random_Random_getrandbits, METH_O,
_random_Random_getrandbits__doc__},
static PyObject *
-_random_Random_getrandbits_impl(RandomObject *self, int k);
+_random_Random_getrandbits_impl(RandomObject *self, unsigned long long k);
static PyObject *
_random_Random_getrandbits(RandomObject *self, PyObject *arg)
{
PyObject *return_value = NULL;
- int k;
+ unsigned long long k;
- k = PyLong_AsInt(arg);
- if (k == -1 && PyErr_Occurred()) {
+ if (!_PyLong_UnsignedLongLong_Converter(arg, &k)) {
goto exit;
}
Py_BEGIN_CRITICAL_SECTION(self);
@@ -143,4 +143,4 @@ _random_Random_getrandbits(RandomObject *self, PyObject
*arg)
exit:
return return_value;
}
-/*[clinic end generated code: output=bf49ece1d341b1b6 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=e9a5c68295678cff input=a9049054013a1b77]*/
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 03d618aeedcf0a..be5cbf0872b446 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -874,16 +874,9 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n,
++numsignificantbytes;
}
- /* How many Python int digits do we need? We have
- 8*numsignificantbytes bits, and each Python int digit has
- PyLong_SHIFT bits, so it's the ceiling of the quotient. */
- /* catch overflow before it happens */
- if (numsignificantbytes > (PY_SSIZE_T_MAX - PyLong_SHIFT) / 8) {
- PyErr_SetString(PyExc_OverflowError,
- "byte array too long to convert to int");
- return NULL;
- }
- ndigits = (numsignificantbytes * 8 + PyLong_SHIFT - 1) / PyLong_SHIFT;
+ /* avoid integer overflow */
+ ndigits = numsignificantbytes / PyLong_SHIFT * 8
+ + (numsignificantbytes % PyLong_SHIFT * 8 + PyLong_SHIFT - 1) /
PyLong_SHIFT;
v = _PyLong_New(ndigits);
if (v == NULL)
return NULL;
_______________________________________________
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]