55import time
66import pickle
77import warnings
8+ import logging
89from functools import partial
910from math import log , exp , pi , fsum , sin , factorial
1011from test import support
@@ -619,6 +620,16 @@ def test_genrandbits(self):
619620 self .assertRaises (ValueError , self .gen .getrandbits , 0 )
620621 self .assertRaises (ValueError , self .gen .getrandbits , - 1 )
621622
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+
622633 def test_randbelow_logic (self , _log = log , int = int ):
623634 # check bitcount transition points: 2**i and 2**(i+1)-1
624635 # show that: k = int(1.001 + _log(n, 2))
@@ -640,21 +651,22 @@ def test_randbelow_logic(self, _log=log, int=int):
640651 self .assertEqual (k , numbits ) # note the stronger assertion
641652 self .assertTrue (2 ** k > n > 2 ** (k - 1 )) # note the stronger assertion
642653
643- @unittest .mock .patch ('random.Random.random' )
644- def test_randbelow_overridden_random (self , random_mock ):
654+ def test_randbelow_without_getrandbits (self ):
645655 # Random._randbelow() can only use random() when the built-in one
646656 # has been overridden but no new getrandbits() method was supplied.
647- random_mock .side_effect = random .SystemRandom ().random
648657 maxsize = 1 << random .BPF
649658 with warnings .catch_warnings ():
650659 warnings .simplefilter ("ignore" , UserWarning )
651660 # 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 )
654665 # issue 33203: test that _randbelow raises ValueError on
655666 # n == 0 also in its getrandbits-independent branch.
656667 with self .assertRaises (ValueError ):
657- self .gen ._randbelow (0 , maxsize = maxsize )
668+ self .gen ._randbelow_without_getrandbits (0 , maxsize = maxsize )
669+
658670 # This might be going too far to test a single line, but because of our
659671 # noble aim of achieving 100% test coverage we need to write a case in
660672 # which the following line in Random._randbelow() gets executed:
@@ -672,8 +684,10 @@ def test_randbelow_overridden_random(self, random_mock):
672684 n = 42
673685 epsilon = 0.01
674686 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 )
677691
678692 def test_randrange_bug_1590891 (self ):
679693 start = 1000000000000
@@ -926,6 +940,49 @@ def test_betavariate_return_zero(self, gammavariate_mock):
926940 gammavariate_mock .return_value = 0.0
927941 self .assertEqual (0.0 , random .betavariate (2.71828 , 3.14159 ))
928942
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+
929986class TestModule (unittest .TestCase ):
930987 def testMagicConstants (self ):
931988 self .assertAlmostEqual (random .NV_MAGICCONST , 1.71552776992141 )
@@ -937,13 +994,6 @@ def test__all__(self):
937994 # tests validity but not completeness of the __all__ list
938995 self .assertTrue (set (random .__all__ ) <= set (dir (random )))
939996
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-
947997 @unittest .skipUnless (hasattr (os , "fork" ), "fork() required" )
948998 def test_after_fork (self ):
949999 # Test the global Random instance gets reseeded in child
0 commit comments