bpo-33144: random.Random and subclasses: split _randbelow implementat… · python/cpython@ba3a87a (original) (raw)
`@@ -5,6 +5,7 @@
`
5
5
`import time
`
6
6
`import pickle
`
7
7
`import warnings
`
``
8
`+
import logging
`
8
9
`from functools import partial
`
9
10
`from math import log, exp, pi, fsum, sin, factorial
`
10
11
`from test import support
`
`@@ -619,6 +620,16 @@ def test_genrandbits(self):
`
619
620
`self.assertRaises(ValueError, self.gen.getrandbits, 0)
`
620
621
`self.assertRaises(ValueError, self.gen.getrandbits, -1)
`
621
622
``
``
623
`+
def test_randrange_uses_getrandbits(self):
`
``
624
`+
Verify use of getrandbits by randrange
`
``
625
`+
Use same seed as in the cross-platform repeatability test
`
``
626
`+
in test_genrandbits above.
`
``
627
`+
self.gen.seed(1234567)
`
``
628
`+
If randrange uses getrandbits, it should pick getrandbits(100)
`
``
629
`+
when called with a 100-bits stop argument.
`
``
630
`+
self.assertEqual(self.gen.randrange(2**99),
`
``
631
`+
97904845777343510404718956115)
`
``
632
+
622
633
`def test_randbelow_logic(self, _log=log, int=int):
`
623
634
`# check bitcount transition points: 2i and 2(i+1)-1
`
624
635
`# show that: k = int(1.001 + _log(n, 2))
`
`@@ -640,21 +651,22 @@ def test_randbelow_logic(self, _log=log, int=int):
`
640
651
`self.assertEqual(k, numbits) # note the stronger assertion
`
641
652
`self.assertTrue(2k > n > 2(k-1)) # note the stronger assertion
`
642
653
``
643
``
`-
@unittest.mock.patch('random.Random.random')
`
644
``
`-
def test_randbelow_overridden_random(self, random_mock):
`
``
654
`+
def test_randbelow_without_getrandbits(self):
`
645
655
`# Random._randbelow() can only use random() when the built-in one
`
646
656
`# has been overridden but no new getrandbits() method was supplied.
`
647
``
`-
random_mock.side_effect = random.SystemRandom().random
`
648
657
`maxsize = 1<<random.BPF
`
649
658
`with warnings.catch_warnings():
`
650
659
`warnings.simplefilter("ignore", UserWarning)
`
651
660
`# Population range too large (n >= maxsize)
`
652
``
`-
self.gen._randbelow(maxsize+1, maxsize = maxsize)
`
653
``
`-
self.gen._randbelow(5640, maxsize = maxsize)
`
``
661
`+
self.gen._randbelow_without_getrandbits(
`
``
662
`+
maxsize+1, maxsize=maxsize
`
``
663
`+
)
`
``
664
`+
self.gen._randbelow_without_getrandbits(5640, maxsize=maxsize)
`
654
665
`# issue 33203: test that _randbelow raises ValueError on
`
655
666
`# n == 0 also in its getrandbits-independent branch.
`
656
667
`with self.assertRaises(ValueError):
`
657
``
`-
self.gen._randbelow(0, maxsize=maxsize)
`
``
668
`+
self.gen._randbelow_without_getrandbits(0, maxsize=maxsize)
`
``
669
+
658
670
`# This might be going too far to test a single line, but because of our
`
659
671
`# noble aim of achieving 100% test coverage we need to write a case in
`
660
672
`# which the following line in Random._randbelow() gets executed:
`
`@@ -672,8 +684,10 @@ def test_randbelow_overridden_random(self, random_mock):
`
672
684
`n = 42
`
673
685
`epsilon = 0.01
`
674
686
`limit = (maxsize - (maxsize % n)) / maxsize
`
675
``
`-
random_mock.side_effect = [limit + epsilon, limit - epsilon]
`
676
``
`-
self.gen._randbelow(n, maxsize = maxsize)
`
``
687
`+
with unittest.mock.patch.object(random.Random, 'random') as random_mock:
`
``
688
`+
random_mock.side_effect = [limit + epsilon, limit - epsilon]
`
``
689
`+
self.gen._randbelow_without_getrandbits(n, maxsize=maxsize)
`
``
690
`+
self.assertEqual(random_mock.call_count, 2)
`
677
691
``
678
692
`def test_randrange_bug_1590891(self):
`
679
693
`start = 1000000000000
`
`@@ -926,6 +940,49 @@ def test_betavariate_return_zero(self, gammavariate_mock):
`
926
940
`gammavariate_mock.return_value = 0.0
`
927
941
`self.assertEqual(0.0, random.betavariate(2.71828, 3.14159))
`
928
942
``
``
943
`+
class TestRandomSubclassing(unittest.TestCase):
`
``
944
`+
def test_random_subclass_with_kwargs(self):
`
``
945
`+
SF bug #1486663 -- this used to erroneously raise a TypeError
`
``
946
`+
class Subclass(random.Random):
`
``
947
`+
def init(self, newarg=None):
`
``
948
`+
random.Random.init(self)
`
``
949
`+
Subclass(newarg=1)
`
``
950
+
``
951
`+
def test_subclasses_overriding_methods(self):
`
``
952
`+
Subclasses with an overridden random, but only the original
`
``
953
`+
getrandbits method should not rely on getrandbits in for randrange,
`
``
954
`+
but should use a getrandbits-independent implementation instead.
`
``
955
+
``
956
`+
subclass providing its own random and getrandbits methods
`
``
957
`+
like random.SystemRandom does => keep relying on getrandbits for
`
``
958
`+
randrange
`
``
959
`+
class SubClass1(random.Random):
`
``
960
`+
def random(self):
`
``
961
`+
return super().random()
`
``
962
+
``
963
`+
def getrandbits(self, n):
`
``
964
`+
logging.getLogger('getrandbits').info('used getrandbits')
`
``
965
`+
return super().getrandbits(n)
`
``
966
`+
with self.assertLogs('getrandbits'):
`
``
967
`+
SubClass1().randrange(42)
`
``
968
+
``
969
`+
subclass providing only random => can only use random for randrange
`
``
970
`+
class SubClass2(random.Random):
`
``
971
`+
def random(self):
`
``
972
`+
logging.getLogger('random').info('used random')
`
``
973
`+
return super().random()
`
``
974
`+
with self.assertLogs('random'):
`
``
975
`+
SubClass2().randrange(42)
`
``
976
+
``
977
`+
subclass defining getrandbits to complement its inherited random
`
``
978
`+
=> can now rely on getrandbits for randrange again
`
``
979
`+
class SubClass3(SubClass2):
`
``
980
`+
def getrandbits(self, n):
`
``
981
`+
logging.getLogger('getrandbits').info('used getrandbits')
`
``
982
`+
return super().getrandbits(n)
`
``
983
`+
with self.assertLogs('getrandbits'):
`
``
984
`+
SubClass3().randrange(42)
`
``
985
+
929
986
`class TestModule(unittest.TestCase):
`
930
987
`def testMagicConstants(self):
`
931
988
`self.assertAlmostEqual(random.NV_MAGICCONST, 1.71552776992141)
`
`@@ -937,13 +994,6 @@ def test__all__(self):
`
937
994
`# tests validity but not completeness of the all list
`
938
995
`self.assertTrue(set(random.all) <= set(dir(random)))
`
939
996
``
940
``
`-
def test_random_subclass_with_kwargs(self):
`
941
``
`-
SF bug #1486663 -- this used to erroneously raise a TypeError
`
942
``
`-
class Subclass(random.Random):
`
943
``
`-
def init(self, newarg=None):
`
944
``
`-
random.Random.init(self)
`
945
``
`-
Subclass(newarg=1)
`
946
``
-
947
997
`@unittest.skipUnless(hasattr(os, "fork"), "fork() required")
`
948
998
`def test_after_fork(self):
`
949
999
`# Test the global Random instance gets reseeded in child
`