From 73d3de0a88457af94a42b982abf87a18ee237dfc Mon Sep 17 00:00:00 2001 From: pctablet505 Date: Thu, 16 Jul 2026 21:57:52 +0530 Subject: [PATCH 1/2] Reset Polyfactory's random state on each test Polyfactory maintains its own default random.Random() instance (polyfactory.constants.DEFAULT_RANDOM) separate from Faker's, so it wasn't being reseeded alongside the other supported libraries. Follows the existing pattern used for factory_boy, Faker, Model Bakery, and NumPy: reseed if the library is importable. Closes #720. --- CHANGELOG.rst | 4 ++++ README.rst | 5 +++++ pyproject.toml | 1 + src/pytest_randomly/__init__.py | 11 +++++++++++ tests/test_pytest_randomly.py | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d6084c5..af1b3a6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,10 @@ Changelog ========= +* Reset `Polyfactory `__’s default random state at the start of every test, if it is installed. + + Thanks to davidszotten for the request in `Issue #720 `__. + 4.1.0 (2026-04-20) ------------------ diff --git a/README.rst b/README.rst index b9890eb..d497bb4 100644 --- a/README.rst +++ b/README.rst @@ -64,6 +64,11 @@ All of these features are on by default but can be disabled with flags. Only its `legacy random state `__ is affected. + * `Polyfactory `__ + + Only the default random generator is affected. Factories that have called + ``seed_random()`` with their own seed are unaffected. + * If additional random generators are used, they can be registered under the ``pytest_randomly.random_seeder`` `entry point `_ and diff --git a/pyproject.toml b/pyproject.toml index b47ddb3..6fb577f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ test = [ "faker", "model-bakery>=1.13", "numpy", + "polyfactory", "pytest>=9", "pytest-xdist", ] diff --git a/src/pytest_randomly/__init__.py b/src/pytest_randomly/__init__.py index 1888f87..e8958f4 100644 --- a/src/pytest_randomly/__init__.py +++ b/src/pytest_randomly/__init__.py @@ -54,6 +54,14 @@ except ImportError: # pragma: no cover have_numpy = False +# polyfactory +try: + from polyfactory.constants import DEFAULT_RANDOM as polyfactory_random + + have_polyfactory = True +except ImportError: # pragma: no cover + have_polyfactory = False + def make_seed() -> int: return random.Random().getrandbits(32) @@ -157,6 +165,9 @@ def _reseed(config: Config, offset: int = 0) -> int: if have_numpy: # pragma: no branch np_random.seed(seed % 2**32) + if have_polyfactory: # pragma: no branch + polyfactory_random.setstate(random_state) + if entrypoint_reseeds is None: eps = entry_points(group="pytest_randomly.random_seeder") entrypoint_reseeds = [e.load() for e in eps] diff --git a/tests/test_pytest_randomly.py b/tests/test_pytest_randomly.py index 2846688..e4582f8 100644 --- a/tests/test_pytest_randomly.py +++ b/tests/test_pytest_randomly.py @@ -696,6 +696,39 @@ def test_one(): out.assert_outcomes(passed=1) +def test_polyfactory(ourtester): + """ + Check that Polyfactory's default random generator is reset between tests. + """ + ourtester.makepyfile( + test_one=""" + from dataclasses import dataclass + + from polyfactory.factories import DataclassFactory + + + @dataclass + class Foo: + x: int + + + class FooFactory(DataclassFactory[Foo]): + __model__ = Foo + + + def test_a(): + assert FooFactory.build().x == 2927 + + + def test_b(): + assert FooFactory.build().x == 1908 + """ + ) + + out = ourtester.runpytest("--randomly-seed=1") + out.assert_outcomes(passed=2) + + def test_failing_import(testdir): """Test with pytest raising CollectError or ImportError. From 208e1ca973f42aa6e2a3accd7b4f5ac4610d497c Mon Sep 17 00:00:00 2001 From: pctablet505 Date: Thu, 16 Jul 2026 22:37:06 +0530 Subject: [PATCH 2/2] Make test_polyfactory actually exercise DEFAULT_RANDOM The int field was generated via Polyfactory's Faker instance, whose random generator pytest-randomly already reseeds independently of this change, so the test passed whether or not DEFAULT_RANDOM was reset. Switch to a Union-typed field so the assertion depends on DEFAULT_RANDOM.choice() picking which type to build, which does regress to a different value if the reseed is removed. --- tests/test_pytest_randomly.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/test_pytest_randomly.py b/tests/test_pytest_randomly.py index e4582f8..73226c6 100644 --- a/tests/test_pytest_randomly.py +++ b/tests/test_pytest_randomly.py @@ -699,17 +699,24 @@ def test_one(): def test_polyfactory(ourtester): """ Check that Polyfactory's default random generator is reset between tests. + + Uses a Union-typed field so the assertion depends on + DEFAULT_RANDOM.choice() picking which type to generate. A plain ``int`` + field wouldn't do: Polyfactory generates those via its Faker instance, + whose random generator pytest-randomly already reseeds separately, so + such a test would pass even without resetting DEFAULT_RANDOM. """ ourtester.makepyfile( test_one=""" from dataclasses import dataclass + from typing import Union from polyfactory.factories import DataclassFactory @dataclass class Foo: - x: int + x: Union[int, str, float, bytes] class FooFactory(DataclassFactory[Foo]): @@ -717,11 +724,15 @@ class FooFactory(DataclassFactory[Foo]): def test_a(): - assert FooFactory.build().x == 2927 + value = FooFactory.build().x + assert isinstance(value, str) + assert value == 'jKdWckZoDYuPmqHSsjBh' def test_b(): - assert FooFactory.build().x == 1908 + value = FooFactory.build().x + assert isinstance(value, int) + assert value == 1908 """ )