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: 2**i 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 (2 ** k > 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
0 commit comments