Skip to content
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

Generic provider does not have a per instance random generator. #1150

Closed
ngnpope opened this issue Jan 6, 2022 · 1 comment · Fixed by #1154
Closed

Generic provider does not have a per instance random generator. #1150

ngnpope opened this issue Jan 6, 2022 · 1 comment · Fixed by #1154
Assignees
Labels
bug Unexpected behaviour and bugs

Comments

@ngnpope
Copy link
Contributor

ngnpope commented Jan 6, 2022

Bug report

What's wrong

  • BaseProvider calls .reseed() from .__init__() but only when seed is not None.

    def __init__(self, *, seed: Seed = None, **kwargs: Any) -> None:
    """Initialize attributes.
    Keep in mind, that locale-independent data providers will work
    only with keyword-only arguments since version 5.0.0.
    :param seed: Seed for random.
    When set to `None` the current system time is used.
    """
    self.seed = seed
    self.random = random
    if seed is not None:
    self.reseed(seed)
    def reseed(self, seed: Seed = None) -> None:
    """Reseed the internal random generator.
    In case we use the default seed, we need to create a per instance
    random generator, in this case two providers with the same seed
    will always return the same values.
    :param seed: Seed for random.
    When set to `None` the current system time is used.
    """
    if self.random is random:
    self.random = Random()
    self.seed = seed
    self.random.seed(self.seed)

  • Generic overrides .reseed() and reseeds its sub-providers. However, it doesn't call super().reseed(seed) which means that it never creates its own per-instance random, nor does it reseed it. This means that the random instance on Generic cannot be used reproducibly.

    def reseed(self, seed: Seed = None) -> None:
    """Reseed the internal random generator.
    Overrides method `BaseProvider.reseed()`.
    :param seed: Seed for random.
    """
    for attr in self.__dir__():
    try:
    provider = getattr(self, attr)
    provider.reseed(seed)
    except AttributeError:
    continue

How is that should be

  • BaseProvider.reseed() should always be called from BaseProvider.__init__(), even when seed is None to ensure that a per-instance random is added. (I don't see why you wouldn't always want this.) And would it then make sense to always do self.random = Random() in BaseProvider.__init__() and not in BaseProvider.reseed()?

    class BaseProvider:
        ...
    
        def __init__(self, *, seed: Seed = None, **kwargs: Any) -> None:
            self.seed = seed
            self.random = Random()
            self.reseed(seed)
    
        def reseed(self, seed: Seed = None) -> None:
            self.seed = seed
            self.random.seed(seed)
    
        ...
  • Generic.reseed() should call super().reseed(seed).

@lk-geimfari
Copy link
Owner

@ngnpope Can you please create PR? I would happy to merge it and create a new release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Unexpected behaviour and bugs
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants