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

`