Skip to content

Commit c880606

Browse files
Copilotmmcky
andcommitted
Simplify Timer API by removing time_run() method
- Remove _RunTimer class and time_run() method as requested - Context manager now only supports single runs (runs=1) - Multiple runs only supported via timeit() method - Update tests to reflect simplified API - Maintain full backward compatibility for existing usage patterns Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com>
1 parent c0fac9d commit c880606

2 files changed

Lines changed: 16 additions & 115 deletions

File tree

quantecon/util/tests/test_timing.py

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -250,55 +250,14 @@ def test_func():
250250
assert len(timer.elapsed) == 2
251251
assert timer.average is not None
252252

253-
def test_time_run_context_manager(self):
254-
"""Test time_run() context manager for manual timing of multiple runs."""
253+
def test_context_manager_multiple_runs_error(self):
254+
"""Test that context manager usage raises error when runs > 1."""
255255
timer = Timer(runs=3, silent=True)
256256

257-
with timer:
258-
for i in range(3):
259-
with timer.time_run():
260-
time.sleep(self.sleep_time)
261-
262-
# Check that we collected all runs
263-
assert timer.elapsed is not None
264-
assert isinstance(timer.elapsed, list)
265-
assert len(timer.elapsed) == 3
266-
assert timer.minimum is not None
267-
assert timer.maximum is not None
268-
assert timer.average is not None
269-
270-
# Check timing accuracy
271-
for run_time in timer.elapsed:
272-
assert_allclose(run_time, self.sleep_time, atol=0.05, rtol=2)
273-
274-
# Check statistics
275-
assert_allclose(timer.average, self.sleep_time, atol=0.05, rtol=2)
276-
assert timer.minimum <= timer.average <= timer.maximum
277-
278-
def test_time_run_single_run_error(self):
279-
"""Test that time_run() raises error when runs=1."""
280-
timer = Timer(runs=1, silent=True)
281-
282257
try:
283-
timer.time_run()
258+
with timer:
259+
time.sleep(self.sleep_time)
284260
assert False, "Should have raised RuntimeError"
285261
except RuntimeError as e:
286-
assert "time_run() is only available when runs > 1" in str(e)
287-
288-
def test_time_run_partial_runs(self):
289-
"""Test time_run() with partial completion of runs."""
290-
timer = Timer(runs=5, silent=True)
291-
292-
with timer:
293-
# Only complete 3 out of 5 runs
294-
for i in range(3):
295-
with timer.time_run():
296-
time.sleep(self.sleep_time)
297-
298-
# Should have recorded the 3 completed runs
299-
assert len(timer._run_times) == 3
300-
# But final statistics should not be set since not all runs completed
301-
assert timer.elapsed is None
302-
assert timer.minimum is None
303-
assert timer.maximum is None
304-
assert timer.average is None
262+
assert "Context manager usage is only supported for single runs" in str(e)
263+

quantecon/util/timing.py

Lines changed: 10 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,6 @@
66
import numpy as np
77

88

9-
class _RunTimer:
10-
"""Internal context manager for individual run timing."""
11-
12-
def __init__(self, parent_timer):
13-
self.parent = parent_timer
14-
self._start_time = None
15-
16-
def __enter__(self):
17-
self._start_time = time.time()
18-
return self
19-
20-
def __exit__(self, exc_type, exc_val, exc_tb):
21-
end_time = time.time()
22-
run_time = end_time - self._start_time
23-
run_number = len(self.parent._run_times) + 1
24-
25-
self.parent._run_times.append(run_time)
26-
27-
if not self.parent.silent:
28-
self.parent._print_single_run(run_number, run_time)
29-
309

3110
class __Timer__:
3211
"""Computes elapsed time, between tic, tac, and toc.
@@ -249,11 +228,11 @@ class Timer:
249228
>>> print(f"Average: {timer.average:.4f}s")
250229
Average: 0.0101s
251230
252-
Multiple runs with context manager and manual timing:
253-
>>> with Timer(runs=3) as timer:
254-
... for i in range(3):
255-
... with timer.time_run():
256-
... time.sleep(0.01)
231+
Multiple runs with callable:
232+
>>> def my_function():
233+
... time.sleep(0.01)
234+
>>> timer = Timer(runs=3)
235+
>>> timer.timeit(my_function)
257236
Run 1/3: 0.01 seconds
258237
Run 2/3: 0.01 seconds
259238
Run 3/3: 0.01 seconds
@@ -271,7 +250,6 @@ def __init__(self, message="", precision=2, unit="seconds", silent=False, runs=1
271250
self.maximum = None
272251
self.average = None
273252
self._start_time = None
274-
self._run_times = []
275253

276254
# Validate unit
277255
valid_units = ["seconds", "milliseconds", "microseconds"]
@@ -283,13 +261,8 @@ def __init__(self, message="", precision=2, unit="seconds", silent=False, runs=1
283261
raise ValueError("runs must be a positive integer")
284262

285263
def __enter__(self):
286-
if self.runs == 1:
287-
# Single run mode - start timing immediately
288-
self._start_time = time.time()
289-
return self
290-
else:
291-
# Multiple runs mode - don't start timing until timeit() is called
292-
return self
264+
self._start_time = time.time()
265+
return self
293266

294267
def __exit__(self, exc_type, exc_val, exc_tb):
295268
if self.runs == 1:
@@ -300,15 +273,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
300273
if not self.silent:
301274
self._print_elapsed()
302275
else:
303-
# Multiple runs mode - print summary if runs were completed
304-
if len(self._run_times) == self.runs:
305-
self.elapsed = self._run_times.copy()
306-
self.minimum = min(self._run_times)
307-
self.maximum = max(self._run_times)
308-
self.average = sum(self._run_times) / len(self._run_times)
309-
310-
if not self.silent:
311-
self._print_multiple_runs_summary()
276+
# Multiple runs mode - context manager not supported, only timeit()
277+
raise RuntimeError("Context manager usage is only supported for single runs (runs=1). For multiple runs, use the timeit() method.")
312278

313279
def timeit(self, func, *args, **kwargs):
314280
"""
@@ -349,31 +315,7 @@ def timeit(self, func, *args, **kwargs):
349315

350316
if not self.silent:
351317
self._print_multiple_runs_summary()
352-
353-
def time_run(self):
354-
"""
355-
Context manager for timing individual runs in multiple runs mode.
356-
Only available when runs > 1.
357-
358-
Returns
359-
-------
360-
_RunTimer
361-
A context manager for timing a single run
362-
363-
Examples
364-
--------
365-
>>> with Timer(runs=3, silent=True) as timer:
366-
... for i in range(3):
367-
... with timer.time_run():
368-
... time.sleep(0.01)
369-
>>> len(timer.elapsed)
370-
3
371-
"""
372-
if self.runs == 1:
373-
raise RuntimeError("time_run() is only available when runs > 1. Use the Timer context manager directly for single runs.")
374-
375-
return _RunTimer(self)
376-
318+
377319
def _print_single_run(self, run_number, run_time):
378320
"""Print timing for a single run in multiple runs mode."""
379321
# Convert to requested unit

0 commit comments

Comments
 (0)