99import platform
1010import re
1111import sys
12- import time
12+ import time as _time_module
1313import warnings
1414from pathlib import Path
1515from typing import TYPE_CHECKING , Any , Callable
@@ -74,6 +74,125 @@ class UnexpectedError(Exception):
7474 resource .setrlimit (resource .RLIMIT_AS , (memory_limit , memory_limit ))
7575
7676
77+ # Store references to original functions before any patching
78+ _ORIGINAL_TIME_TIME = _time_module .time
79+ _ORIGINAL_PERF_COUNTER = _time_module .perf_counter
80+ _ORIGINAL_TIME_SLEEP = _time_module .sleep
81+
82+
83+ # Apply deterministic patches for reproducible test execution
84+ def _apply_deterministic_patches () -> None :
85+ """Apply patches to make all sources of randomness deterministic."""
86+ import datetime
87+ import random
88+ import time
89+ import uuid
90+
91+ # Store original functions (these are already saved globally above)
92+ _original_time = time .time
93+ _original_perf_counter = time .perf_counter
94+ _original_datetime_now = datetime .datetime .now
95+ _original_datetime_utcnow = datetime .datetime .utcnow
96+ _original_uuid4 = uuid .uuid4
97+ _original_uuid1 = uuid .uuid1
98+ _original_random = random .random
99+
100+ # Fixed deterministic values
101+ fixed_timestamp = 1609459200.0 # 2021-01-01 00:00:00 UTC
102+ fixed_datetime = datetime .datetime (2021 , 1 , 1 , 0 , 0 , 0 , tzinfo = datetime .timezone .utc )
103+ fixed_uuid = uuid .UUID ("12345678-1234-5678-9abc-123456789012" )
104+
105+ # Counter for perf_counter to maintain relative timing
106+ _perf_counter_start = fixed_timestamp
107+ _perf_counter_calls = 0
108+
109+ def mock_time_time () -> float :
110+ """Return fixed timestamp while preserving performance characteristics."""
111+ _original_time () # Maintain performance characteristics
112+ return fixed_timestamp
113+
114+ def mock_perf_counter () -> float :
115+ """Return incrementing counter for relative timing."""
116+ nonlocal _perf_counter_calls
117+ _original_perf_counter () # Maintain performance characteristics
118+ _perf_counter_calls += 1
119+ return _perf_counter_start + (_perf_counter_calls * 0.001 ) # Increment by 1ms each call
120+
121+ def mock_datetime_now (tz : datetime .timezone | None = None ) -> datetime .datetime :
122+ """Return fixed datetime while preserving performance characteristics."""
123+ _original_datetime_now (tz ) # Maintain performance characteristics
124+ if tz is None :
125+ return fixed_datetime
126+ return fixed_datetime .replace (tzinfo = tz )
127+
128+ def mock_datetime_utcnow () -> datetime .datetime :
129+ """Return fixed UTC datetime while preserving performance characteristics."""
130+ _original_datetime_utcnow () # Maintain performance characteristics
131+ return fixed_datetime
132+
133+ def mock_uuid4 () -> uuid .UUID :
134+ """Return fixed UUID4 while preserving performance characteristics."""
135+ _original_uuid4 () # Maintain performance characteristics
136+ return fixed_uuid
137+
138+ def mock_uuid1 (node : int | None = None , clock_seq : int | None = None ) -> uuid .UUID :
139+ """Return fixed UUID1 while preserving performance characteristics."""
140+ _original_uuid1 (node , clock_seq ) # Maintain performance characteristics
141+ return fixed_uuid
142+
143+ def mock_random () -> float :
144+ """Return deterministic random value while preserving performance characteristics."""
145+ _original_random () # Maintain performance characteristics
146+ return 0.123456789 # Fixed random value
147+
148+ # Apply patches
149+ time .time = mock_time_time
150+ time .perf_counter = mock_perf_counter
151+ uuid .uuid4 = mock_uuid4
152+ uuid .uuid1 = mock_uuid1
153+
154+ # Seed random module for other random functions
155+ random .seed (42 )
156+ random .random = mock_random
157+
158+ # For datetime, we need to use a different approach since we can't patch class methods
159+ # Store original methods for potential later use
160+ import builtins
161+
162+ builtins ._original_datetime_now = _original_datetime_now # noqa: SLF001
163+ builtins ._original_datetime_utcnow = _original_datetime_utcnow # noqa: SLF001
164+ builtins ._mock_datetime_now = mock_datetime_now # noqa: SLF001
165+ builtins ._mock_datetime_utcnow = mock_datetime_utcnow # noqa: SLF001
166+
167+ # Patch numpy.random if available
168+ try :
169+ import numpy as np
170+
171+ # Use modern numpy random generator approach
172+ np .random .default_rng (42 )
173+ np .random .seed (42 ) # Keep legacy seed for compatibility # noqa: NPY002
174+ except ImportError :
175+ pass
176+
177+ # Patch os.urandom if needed
178+ try :
179+ import os
180+
181+ _original_urandom = os .urandom
182+
183+ def mock_urandom (n : int ) -> bytes :
184+ _original_urandom (n ) # Maintain performance characteristics
185+ return b"\x42 " * n # Fixed bytes
186+
187+ os .urandom = mock_urandom
188+ except (ImportError , AttributeError ):
189+ pass
190+
191+
192+ # Note: Deterministic patches are applied conditionally, not globally
193+ # They should only be applied when running CodeFlash optimization tests
194+
195+
77196def pytest_addoption (parser : Parser ) -> None :
78197 """Add command line options."""
79198 pytest_loops = parser .getgroup ("loops" )
@@ -137,6 +256,9 @@ def pytest_configure(config: Config) -> None:
137256 config .addinivalue_line ("markers" , "loops(n): run the given test function `n` times." )
138257 config .pluginmanager .register (PytestLoops (config ), PytestLoops .name )
139258
259+ # Apply deterministic patches when the plugin is configured
260+ _apply_deterministic_patches ()
261+
140262
141263class PytestLoops :
142264 name : str = "pytest-loops"
@@ -157,7 +279,7 @@ def pytest_runtestloop(self, session: Session) -> bool:
157279 if session .config .option .collectonly :
158280 return True
159281
160- start_time : float = time . time ()
282+ start_time : float = _ORIGINAL_TIME_TIME ()
161283 total_time : float = self ._get_total_time (session )
162284
163285 count : int = 0
@@ -184,7 +306,7 @@ def pytest_runtestloop(self, session: Session) -> bool:
184306 raise session .Interrupted (session .shouldstop )
185307 if self ._timed_out (session , start_time , count ):
186308 break # exit loop
187- time . sleep (self ._get_delay_time (session ))
309+ _ORIGINAL_TIME_SLEEP (self ._get_delay_time (session ))
188310 return True
189311
190312 def _clear_lru_caches (self , item : pytest .Item ) -> None :
@@ -283,7 +405,7 @@ def _timed_out(self, session: Session, start_time: float, count: int) -> bool:
283405 """
284406 return count >= session .config .option .codeflash_max_loops or (
285407 count >= session .config .option .codeflash_min_loops
286- and time . time () - start_time > self ._get_total_time (session )
408+ and _ORIGINAL_TIME_TIME () - start_time > self ._get_total_time (session )
287409 )
288410
289411 @pytest .fixture
0 commit comments