mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #4748 from jyj0816/random
Fix int.from_bytes and Update random.py and test/test_random.py from CPython v3.11.2
This commit is contained in:
82
Lib/random.py
vendored
82
Lib/random.py
vendored
@@ -167,15 +167,11 @@ class Random(_random.Random):
|
||||
elif version == 2 and isinstance(a, (str, bytes, bytearray)):
|
||||
if isinstance(a, str):
|
||||
a = a.encode()
|
||||
a = int.from_bytes(a + _sha512(a).digest(), 'big')
|
||||
a = int.from_bytes(a + _sha512(a).digest())
|
||||
|
||||
elif not isinstance(a, (type(None), int, float, str, bytes, bytearray)):
|
||||
_warn('Seeding based on hashing is deprecated\n'
|
||||
'since Python 3.9 and will be removed in a subsequent '
|
||||
'version. The only \n'
|
||||
'supported seed types are: None, '
|
||||
'int, float, str, bytes, and bytearray.',
|
||||
DeprecationWarning, 2)
|
||||
raise TypeError('The only supported seed types are: None,\n'
|
||||
'int, float, str, bytes, and bytearray.')
|
||||
|
||||
super().seed(a)
|
||||
self.gauss_next = None
|
||||
@@ -250,10 +246,8 @@ class Random(_random.Random):
|
||||
break
|
||||
|
||||
def _randbelow_with_getrandbits(self, n):
|
||||
"Return a random int in the range [0,n). Returns 0 if n==0."
|
||||
"Return a random int in the range [0,n). Defined for 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
|
||||
@@ -262,7 +256,7 @@ class Random(_random.Random):
|
||||
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.
|
||||
"""Return a random int in the range [0,n). Defined for n > 0.
|
||||
|
||||
The implementation does not use getrandbits, but only random.
|
||||
"""
|
||||
@@ -273,8 +267,6 @@ class Random(_random.Random):
|
||||
"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()
|
||||
@@ -303,10 +295,10 @@ class Random(_random.Random):
|
||||
## -------------------- integer methods -------------------
|
||||
|
||||
def randrange(self, start, stop=None, step=_ONE):
|
||||
"""Choose a random item from range(start, stop[, step]).
|
||||
"""Choose a random item from range(stop) or range(start, stop[, step]).
|
||||
|
||||
This fixes the problem with randint() which includes the
|
||||
endpoint; in Python this is usually not what you want.
|
||||
Roughly equivalent to ``choice(range(start, stop, step))`` but
|
||||
supports arbitrarily large ranges and is optimized for common cases.
|
||||
|
||||
"""
|
||||
|
||||
@@ -387,37 +379,24 @@ class Random(_random.Random):
|
||||
|
||||
def choice(self, seq):
|
||||
"""Choose a random element from a non-empty sequence."""
|
||||
# raises IndexError if seq is empty
|
||||
|
||||
# As an accommodation for NumPy, we don't use "if not seq"
|
||||
# because bool(numpy.array()) raises a ValueError.
|
||||
if not len(seq):
|
||||
raise IndexError('Cannot choose from an empty sequence')
|
||||
return seq[self._randbelow(len(seq))]
|
||||
|
||||
def shuffle(self, x, random=None):
|
||||
"""Shuffle list x in place, and return None.
|
||||
def shuffle(self, x):
|
||||
"""Shuffle list x in place, and return None."""
|
||||
|
||||
Optional argument random is a 0-argument function returning a
|
||||
random float in [0.0, 1.0); if it is the default None, the
|
||||
standard random.random will be used.
|
||||
|
||||
"""
|
||||
|
||||
if random is None:
|
||||
randbelow = self._randbelow
|
||||
for i in reversed(range(1, len(x))):
|
||||
# pick an element in x[:i+1] with which to exchange x[i]
|
||||
j = randbelow(i + 1)
|
||||
x[i], x[j] = x[j], x[i]
|
||||
else:
|
||||
_warn('The *random* parameter to shuffle() has been deprecated\n'
|
||||
'since Python 3.9 and will be removed in a subsequent '
|
||||
'version.',
|
||||
DeprecationWarning, 2)
|
||||
floor = _floor
|
||||
for i in reversed(range(1, len(x))):
|
||||
# pick an element in x[:i+1] with which to exchange x[i]
|
||||
j = floor(random() * (i + 1))
|
||||
x[i], x[j] = x[j], x[i]
|
||||
randbelow = self._randbelow
|
||||
for i in reversed(range(1, len(x))):
|
||||
# pick an element in x[:i+1] with which to exchange x[i]
|
||||
j = randbelow(i + 1)
|
||||
x[i], x[j] = x[j], x[i]
|
||||
|
||||
def sample(self, population, k, *, counts=None):
|
||||
"""Chooses k unique random elements from a population sequence or set.
|
||||
"""Chooses k unique random elements from a population sequence.
|
||||
|
||||
Returns a new list containing elements from the population while
|
||||
leaving the original population unchanged. The resulting list is
|
||||
@@ -470,13 +449,8 @@ class Random(_random.Random):
|
||||
# causing them to eat more entropy than necessary.
|
||||
|
||||
if not isinstance(population, _Sequence):
|
||||
if isinstance(population, _Set):
|
||||
_warn('Sampling from a set deprecated\n'
|
||||
'since Python 3.9 and will be removed in a subsequent version.',
|
||||
DeprecationWarning, 2)
|
||||
population = tuple(population)
|
||||
else:
|
||||
raise TypeError("Population must be a sequence. For dicts or sets, use sorted(d).")
|
||||
raise TypeError("Population must be a sequence. "
|
||||
"For dicts or sets, use sorted(d).")
|
||||
n = len(population)
|
||||
if counts is not None:
|
||||
cum_counts = list(_accumulate(counts))
|
||||
@@ -580,7 +554,7 @@ class Random(_random.Random):
|
||||
low, high = high, low
|
||||
return low + (high - low) * _sqrt(u * c)
|
||||
|
||||
def normalvariate(self, mu, sigma):
|
||||
def normalvariate(self, mu=0.0, sigma=1.0):
|
||||
"""Normal distribution.
|
||||
|
||||
mu is the mean, and sigma is the standard deviation.
|
||||
@@ -601,7 +575,7 @@ class Random(_random.Random):
|
||||
break
|
||||
return mu + z * sigma
|
||||
|
||||
def gauss(self, mu, sigma):
|
||||
def gauss(self, mu=0.0, sigma=1.0):
|
||||
"""Gaussian distribution.
|
||||
|
||||
mu is the mean, and sigma is the standard deviation. This is
|
||||
@@ -833,15 +807,15 @@ class SystemRandom(Random):
|
||||
"""
|
||||
|
||||
def random(self):
|
||||
"""Get the next random number in the range [0.0, 1.0)."""
|
||||
return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF
|
||||
"""Get the next random number in the range 0.0 <= X < 1.0."""
|
||||
return (int.from_bytes(_urandom(7)) >> 3) * RECIP_BPF
|
||||
|
||||
def getrandbits(self, k):
|
||||
"""getrandbits(k) -> x. Generates an int with k random bits."""
|
||||
if k < 0:
|
||||
raise ValueError('number of bits must be non-negative')
|
||||
numbytes = (k + 7) // 8 # bits / 8 and rounded up
|
||||
x = int.from_bytes(_urandom(numbytes), 'big')
|
||||
x = int.from_bytes(_urandom(numbytes))
|
||||
return x >> (numbytes * 8 - k) # trim excess bits
|
||||
|
||||
def randbytes(self, n):
|
||||
|
||||
77
Lib/test/test_random.py
vendored
77
Lib/test/test_random.py
vendored
@@ -52,12 +52,11 @@ class TestBasicOps:
|
||||
self.gen.seed(arg)
|
||||
|
||||
for arg in [1+2j, tuple('abc'), MySeed()]:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
with self.assertRaises(TypeError):
|
||||
self.gen.seed(arg)
|
||||
|
||||
for arg in [list(range(3)), dict(one=1)]:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertRaises(TypeError, self.gen.seed, arg)
|
||||
self.assertRaises(TypeError, self.gen.seed, arg)
|
||||
self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4)
|
||||
self.assertRaises(TypeError, type(self.gen), [])
|
||||
|
||||
@@ -110,15 +109,6 @@ class TestBasicOps:
|
||||
self.assertTrue(lst != shuffled_lst)
|
||||
self.assertRaises(TypeError, shuffle, (1, 2, 3))
|
||||
|
||||
def test_shuffle_random_argument(self):
|
||||
# Test random argument to shuffle.
|
||||
shuffle = self.gen.shuffle
|
||||
mock_random = unittest.mock.Mock(return_value=0.5)
|
||||
seq = bytearray(b'abcdefghijk')
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
shuffle(seq, mock_random)
|
||||
mock_random.assert_called_with()
|
||||
|
||||
def test_choice(self):
|
||||
choice = self.gen.choice
|
||||
with self.assertRaises(IndexError):
|
||||
@@ -126,6 +116,21 @@ class TestBasicOps:
|
||||
self.assertEqual(choice([50]), 50)
|
||||
self.assertIn(choice([25, 75]), [25, 75])
|
||||
|
||||
def test_choice_with_numpy(self):
|
||||
# Accommodation for NumPy arrays which have disabled __bool__().
|
||||
# See: https://github.com/python/cpython/issues/100805
|
||||
choice = self.gen.choice
|
||||
|
||||
class NA(list):
|
||||
"Simulate numpy.array() behavior"
|
||||
def __bool__(self):
|
||||
raise RuntimeError
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
choice(NA([]))
|
||||
self.assertEqual(choice(NA([50])), 50)
|
||||
self.assertIn(choice(NA([25, 75])), [25, 75])
|
||||
|
||||
def test_sample(self):
|
||||
# For the entire allowable range of 0 <= k <= N, validate that
|
||||
# the sample is of the correct length and contains only unique items
|
||||
@@ -169,7 +174,7 @@ class TestBasicOps:
|
||||
self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2)
|
||||
|
||||
def test_sample_on_sets(self):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
with self.assertRaises(TypeError):
|
||||
population = {10, 20, 30, 40, 50, 60, 70}
|
||||
self.gen.sample(population, k=5)
|
||||
|
||||
@@ -391,23 +396,6 @@ class TestBasicOps:
|
||||
restoredseq = [newgen.random() for i in range(10)]
|
||||
self.assertEqual(origseq, restoredseq)
|
||||
|
||||
@test.support.cpython_only
|
||||
def test_bug_41052(self):
|
||||
# _random.Random should not be allowed to serialization
|
||||
import _random
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
r = _random.Random()
|
||||
self.assertRaises(TypeError, pickle.dumps, r, proto)
|
||||
|
||||
@test.support.cpython_only
|
||||
def test_bug_42008(self):
|
||||
# _random.Random should call seed with first element of arg tuple
|
||||
import _random
|
||||
r1 = _random.Random()
|
||||
r1.seed(8675309)
|
||||
r2 = _random.Random(8675309)
|
||||
self.assertEqual(r1.random(), r2.random())
|
||||
|
||||
# TODO: RUSTPYTHON AttributeError: 'super' object has no attribute 'getstate'
|
||||
@unittest.expectedFailure
|
||||
def test_bug_1727780(self):
|
||||
@@ -445,6 +433,10 @@ class TestBasicOps:
|
||||
self.assertRaises(ValueError, self.gen.randbytes, -1)
|
||||
self.assertRaises(TypeError, self.gen.randbytes, 1.0)
|
||||
|
||||
def test_mu_sigma_default_args(self):
|
||||
self.assertIsInstance(self.gen.normalvariate(), float)
|
||||
self.assertIsInstance(self.gen.gauss(), float)
|
||||
|
||||
|
||||
try:
|
||||
random.SystemRandom().random()
|
||||
@@ -592,6 +584,25 @@ class SystemRandom_TestBasicOps(TestBasicOps, unittest.TestCase):
|
||||
self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
|
||||
|
||||
|
||||
class TestRawMersenneTwister(unittest.TestCase):
|
||||
@test.support.cpython_only
|
||||
def test_bug_41052(self):
|
||||
# _random.Random should not be allowed to serialization
|
||||
import _random
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
r = _random.Random()
|
||||
self.assertRaises(TypeError, pickle.dumps, r, proto)
|
||||
|
||||
@test.support.cpython_only
|
||||
def test_bug_42008(self):
|
||||
# _random.Random should call seed with first element of arg tuple
|
||||
import _random
|
||||
r1 = _random.Random()
|
||||
r1.seed(8675309)
|
||||
r2 = _random.Random(8675309)
|
||||
self.assertEqual(r1.random(), r2.random())
|
||||
|
||||
|
||||
class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
|
||||
gen = random.Random()
|
||||
|
||||
@@ -846,10 +857,6 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
|
||||
maxsize+1, maxsize=maxsize
|
||||
)
|
||||
self.gen._randbelow_without_getrandbits(5640, maxsize=maxsize)
|
||||
# issue 33203: test that _randbelow returns zero on
|
||||
# n == 0 also in its getrandbits-independent branch.
|
||||
x = self.gen._randbelow_without_getrandbits(0, maxsize=maxsize)
|
||||
self.assertEqual(x, 0)
|
||||
|
||||
# This might be going too far to test a single line, but because of our
|
||||
# noble aim of achieving 100% test coverage we need to write a case in
|
||||
@@ -1331,7 +1338,7 @@ class TestModule(unittest.TestCase):
|
||||
# tests validity but not completeness of the __all__ list
|
||||
self.assertTrue(set(random.__all__) <= set(dir(random)))
|
||||
|
||||
@unittest.skipUnless(hasattr(os, "fork"), "fork() required")
|
||||
@test.support.requires_fork()
|
||||
def test_after_fork(self):
|
||||
# Test the global Random instance gets reseeded in child
|
||||
r, w = os.pipe()
|
||||
|
||||
@@ -794,6 +794,7 @@ pub struct IntOptions {
|
||||
#[derive(FromArgs)]
|
||||
struct IntFromByteArgs {
|
||||
bytes: PyBytesInner,
|
||||
#[pyarg(any, default = "ArgByteOrder::Big")]
|
||||
byteorder: ArgByteOrder,
|
||||
#[pyarg(named, optional)]
|
||||
signed: OptionalArg<ArgIntoBool>,
|
||||
|
||||
Reference in New Issue
Block a user