diff -r 27adb952813b Doc/library/random.rst --- a/Doc/library/random.rst Sun Nov 13 04:11:37 2011 +0100 +++ b/Doc/library/random.rst Sun Nov 13 17:40:00 2011 +0100 @@ -90,6 +90,14 @@ as an optional part of the API. When available, :meth:`getrandbits` enables :meth:`randrange` to handle arbitrarily large ranges. +.. function:: getrandbytes(n) + + Returns a :class:`bytes` object with *n* random bytes. This method is + supplied with the MersenneTwister generator and some other generators may + also provide it as an optional part of the API. + + .. versionadded:: 3.3 + Functions for integers: diff -r 27adb952813b Lib/test/test_random.py --- a/Lib/test/test_random.py Sun Nov 13 04:11:37 2011 +0100 +++ b/Lib/test/test_random.py Sun Nov 13 17:40:00 2011 +0100 @@ -213,6 +213,26 @@ self.assertRaises(ValueError, self.gen.getrandbits, -1) self.assertRaises(TypeError, self.gen.getrandbits, 10.1) + def test_genrandbytes(self): + # Verify ranges + for n in range(1000): + self.assertEquals(len(self.gen.getrandbytes(n)), n) + + # Verify all bytes active + getbytes = self.gen.getrandbytes + for span in [1, 2, 3, 4, 31, 32, 33, 34]: + cum = set() + for i in range(100): + val = getbytes(span) + cum |= set(i for i in range(span) if val[i]) + self.assertEqual(len(cum), span) + + # Verify argument checking + self.assertRaises(TypeError, self.gen.getrandbytes) + self.assertRaises(TypeError, self.gen.getrandbytes, 1, 2) + self.assertRaises(ValueError, self.gen.getrandbytes, -1) + self.assertRaises(TypeError, self.gen.getrandbytes, 10.1) + def test_randbelow_logic(self, _log=log, int=int): # check bitcount transition points: 2**i and 2**(i+1)-1 # show that: k = int(1.001 + _log(n, 2)) diff -r 27adb952813b Modules/_randommodule.c --- a/Modules/_randommodule.c Sun Nov 13 04:11:37 2011 +0100 +++ b/Modules/_randommodule.c Sun Nov 13 17:40:00 2011 +0100 @@ -411,6 +411,42 @@ } static PyObject * +random_getrandbytes(RandomObject *self, PyObject *args) +{ + Py_ssize_t i, bytes; + char *bytearray; + PyObject *result; + + if (!PyArg_ParseTuple(args, "n:getrandbytes", &bytes)) + return NULL; + + if (bytes < 0) { + PyErr_SetString(PyExc_ValueError, + "number of bytes must be greater than zero"); + return NULL; + } + + result = PyBytes_FromStringAndSize(NULL, bytes); + if (result == NULL) + return NULL; + bytearray = PyBytes_AS_STRING(result); + + i = 0; + while (i < bytes) { + unsigned long r = genrand_int32(self); + bytearray[i++] = (char)r; + if (i < bytes) + bytearray[i++] = (char)(r >> 8); + if (i < bytes) + bytearray[i++] = (char)(r >> 16); + if (i < bytes) + bytearray[i++] = (char)(r >> 24); + } + + return result; +} + +static PyObject * random_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { RandomObject *self; @@ -443,6 +479,9 @@ {"getrandbits", (PyCFunction)random_getrandbits, METH_VARARGS, PyDoc_STR("getrandbits(k) -> x. Generates a long int with " "k random bits.")}, + {"getrandbytes", (PyCFunction)random_getrandbytes, METH_VARARGS, + PyDoc_STR("getrandbytes(n) -> x. Generates a bytes string with " + "n random bytes.")}, {NULL, NULL} /* sentinel */ };