Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-40346: Random subclass randbytes uses getrandbits #19700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed

bpo-40346: Random subclass randbytes uses getrandbits #19700

wants to merge 7 commits into from

Conversation

vstinner
Copy link
Member

@vstinner vstinner commented Apr 24, 2020

Subclass of the random.Random class now get a randbytes() method
implementation which uses the getrandbits() method.

https://bugs.python.org/issue40346

@vstinner
Copy link
Member Author

@vstinner
Copy link
Member Author

I didn't change the documentation. Or do you think that it must be said explicitly that Random subclass implement randbytes() with getrandbits()? My minor documentation issue is that SystemRandom subclasses don't get a randbytes() implementation which uses getrandbits(): SystemRandom subclasses get SystemRandom.randbytes() which calls os.urandom().

Subclasses of the random.Random and random.SystemRandom classes now
get a randbytes() method implementation which uses the getrandbits()
method.
@vstinner
Copy link
Member Author

I didn't change the documentation. (...) My minor documentation issue is that SystemRandom subclasses don't get a randbytes() implementation which uses getrandbits() (...)

Oh, I found a project in the wild which creates a SystemRandom subclass:
https://pypi.org/project/random-xe/

So I modified my PR to change also SystemRandom subclasses, and I completed the documentation.

@vstinner
Copy link
Member Author

vstinner commented Apr 24, 2020

I updated the PR to address @serhiy-storchaka's comments and I added more tests on two other kinds of subclasses.

@serhiy-storchaka
Copy link
Member

I am not sure that pickling the randbytes method of Random subclass works correctly. If it does not work it may be better to use the second option for the randbytes implementation.

super.__init_subclass__ => super().__init_subclass__

Cleanup also tests.
Check SystemRandom.getrandbits, not _random.Random.getrandbits.
@vstinner
Copy link
Member Author

I am not sure that pickling the randbytes method of Random subclass works correctly. If it does not work it may be better to use the second option for the randbytes implementation.

pickle can be used to serialize/deserialize instances. __init_subclass__() is called when a new class is created.

Example:

import random
import pickle

class MyRandom(random.Random):
    def __init__(self):
        super().__init__()
        self.getrandbits_calls = []
        print("init", self)

    def getrandbits(self, n):
        self.getrandbits_calls.append(n)
        return 0

rng = MyRandom()
print(rng.randbytes != random.Random.randbytes)
print()
rng2 = pickle.loads(pickle.dumps(rng))
print(rng2.randbytes != random.Random.randbytes)

Output:

init <__main__.MyRandom object at 0xefaff0>
True

init <__main__.MyRandom object at 0xf2f320>
True

Two MyRandom instances are created, but MyRandom.randbytes is only set once by Random.__init_subclass(MyRandom): when the class is created.

@serhiy-storchaka
Copy link
Member

I meant pickling the method.

pickle.dumps(MyRandom.randbytes)

@vstinner
Copy link
Member Author

I meant pickling the method.
pickle.dumps(MyRandom.randbytes)

Alright, that's broken because you asked me to ensure that Random._randbytes_getrandbits is called "randbytes". I reverted this change, now it works again. Example:

import random
import pickle

class MyRandom(random.Random):
    def __init__(self):
        super().__init__()

    def getrandbits(self, n):
        return 0

rng = MyRandom()
print(MyRandom.randbytes is random.Random._randbytes_getrandbits)
randbytes2 = pickle.loads(pickle.dumps(MyRandom.randbytes))
print(randbytes2 is random.Random._randbytes_getrandbits)

Output:

True
True

@serhiy-storchaka
Copy link
Member

I think we can have this cake and eat it. Define randbytes() in __init_subclass__() and set its __module__ and __qualname__.

@vstinner
Copy link
Member Author

@serhiy-storchaka:

If it does not work it may be better to use the second option for the randbytes implementation.

Which second option? Remove the C implementation and always implement randbytes() using getrandbits() in Lib/random.py?

Well, I proposed a solution to keep the optimization (C implementation) and don't require subclasses to have to override a 6th method (randbytes).

I'm not super excited by adding more special cases __init_subclass__() (this PR). I prefer PR #19631 (random.BaseRandom) approach which uses more "regular Python code".

I let @rhettinger and you decide which approach is the best.

When I read https://bugs.python.org/issue40346 it's not even clear if we support subclassing or not, nor if performance matters or not. I'm now very confused :-( Now at least, we have multiple choices, and I let you decide which one is the best ;-)

@serhiy-storchaka
Copy link
Member

The second option which I mentioned in my comment #19700 (comment) and repeated in #19700 (comment).

@vstinner
Copy link
Member Author

create an internal randbytes function in init_subclass and set its qualname (which depends on the class name) manually.

Well, it seems like there are different ways to implement this change. But I'm not sure that this approach is the best. For now, I will leave the PR as it is.

I'm now waiting for a feedback on https://bugs.python.org/issue40346#msg367202

@vstinner
Copy link
Member Author

I abandon my BaseRandom PR #19631 and this PR since there is no clear consensus on these changes: see https://bugs.python.org/issue40346

I wrote PR #19797 instead: "Remove C implementation of Random.randbytes()".

@vstinner vstinner closed this Apr 29, 2020
@vstinner vstinner deleted the randbytes_subclass branch April 29, 2020 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants