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
Update documentation to note dangers of shared state in random module
#93743
Conversation
.. rather than global version
|
Most changes to Python require a NEWS entry. Please add it using the blurb_it web app or the blurb command-line tool. |
|
You can ignore the bot; purely documentation changes need neither changelog entries nor issue numbers. |
| instance of the :class:`random.Random` class, and thus share state – in | ||
| particular, calls to :meth:`random.set_seed` anywhere in application code | ||
| or any libraries will affect the sequence of random values produced by calls | ||
| to `random.*` methods in the same process, which can lead to unexpected results. | ||
| You should consider instantiating your own instance or instances of :class:`Random` | ||
| to get generators that don't share state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace is making the CI check fail:
| instance of the :class:`random.Random` class, and thus share state – in | |
| particular, calls to :meth:`random.set_seed` anywhere in application code | |
| or any libraries will affect the sequence of random values produced by calls | |
| to `random.*` methods in the same process, which can lead to unexpected results. | |
| You should consider instantiating your own instance or instances of :class:`Random` | |
| to get generators that don't share state. | |
| instance of the :class:`random.Random` class, and thus share state – in | |
| particular, calls to :meth:`random.set_seed` anywhere in application code | |
| or any libraries will affect the sequence of random values produced by calls | |
| to `random.*` methods in the same process, which can lead to unexpected results. | |
| You should consider instantiating your own instance or instances of :class:`Random` | |
| to get generators that don't share state. |
| instance of the :class:`random.Random` class, and thus share state – in | ||
| particular, calls to :meth:`random.set_seed` anywhere in application code | ||
| or any libraries will affect the sequence of random values produced by calls | ||
| to `random.*` methods in the same process, which can lead to unexpected results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| to `random.*` methods in the same process, which can lead to unexpected results. | |
| to ``random.*`` methods in the same process, which can lead to unexpected results. |
or
"other top-level functions in :mod:`random`"
| You should consider instantiating your own instance or instances of :class:`Random` | ||
| to get generators that don't share state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some applications, the sharing of state might not matter -- perhaps provide a heuristic for where I'd want to instantiate my own Random class?
| particular, calls to :meth:`random.set_seed` anywhere in application code | ||
| or any libraries will affect the sequence of random values produced by calls | ||
| to `random.*` methods in the same process, which can lead to unexpected results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not reflowed; See my other suggestion on "random.*"
| particular, calls to :meth:`random.set_seed` anywhere in application code | |
| or any libraries will affect the sequence of random values produced by calls | |
| to `random.*` methods in the same process, which can lead to unexpected results. | |
| particular, a call to :meth:`random.set_seed` in any executed code | |
| will affect the sequence of random values produced, as the random seed is shared. | |
| This affects return values from calls to other `random.*` methods in the same process, potentially leading to unexpected results. |
| @@ -30,8 +30,12 @@ deterministic, it is not suitable for all purposes, and is completely unsuitable | |||
| for cryptographic purposes. | |||
|
|
|||
| The functions supplied by this module are actually bound methods of a hidden | |||
| instance of the :class:`random.Random` class. You can instantiate your own | |||
| instances of :class:`Random` to get generators that don't share state. | |||
| instance of the :class:`random.Random` class, and thus share state – in | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| instance of the :class:`random.Random` class, and thus share state – in | |
| instance of the :class:`Random` class, and thus share state – in |
|
Thank you for the suggestion, but we will decline. In general, we don't modify the docs in response to someone having a bug in their code especially if the essential details have already been covered. Here, the docs already state, "you can instantiate your own instances of Random() to get generators that don’t share state." That gets to the heart of the matter. Our style is to word the docs are affirmatively worded and not fill them with "warnings and dangers" unless a significant security risk is present. It is just a general programming principle that changing a stateful object affects all users and viewers of the object. If a library function calls print() in the middle of a user's print routine, it will commingle the output. There are many examples. There isn't anything special about the random module in the regard. Thanks again for the suggestion, but I think it doesn't improve the docs (and likely wouldn't have prevented your library author from writing the code the way they did). ISTM this intro section reads better without the programming practices side trip. |
|
@rhettinger -- I think it might be useful to at least note that one of the items of shared state is the seed, which a developer on a quick read through may not realise. There isn't a 'how to' guide for random at the moment, so I believe the module documentation is the best place. (Such a guide would probably be useful, but a different matter). A |
Does it though? if two authors calling the library exactly as the docs suggest they should, using
This is fair, I would be more than happy to reword to be more advice than warning but it seems like there is not much point.
TBH I'm not sure that I'm struggling to think of other examples in the stdlib where global state (not just reading/writing from stdin/stdout) is the important (and also not transparent)
I mean if the docs said "this way might be a good idea" I'd like to imagine at least some library authors would pay attention to it, otherwise why are the docs there at all? Sure not all would. and I'll try to avoid what you call "side trips" in future suggestions. |
I think this is better than nothing – I've had a go over at #93755 |
Context: I've been bitten recently by a call to a third-party library function indirectly calling
random.seedand changing the behaviour of my own code. I think it's worth adding bit of extra warning about the dangers of shared state since this can lead to subtle, hard-to-detect and hard-to-explain bugs.I thought this was small enough that it didn't need an issue (clarifying existing docs) but happy to be corrected.