Skip to content

Commit 2628f50

Browse files
Copilotmmcky
andauthored
IMP: Simplify qe.timeit function with verbose parameter and update version to 0.10.1 (#797)
* Initial plan * Implement verbose and results parameters for qe.timeit function Co-authored-by: mmcky <[email protected]> * Remove silent parameter, keep only verbose for output control - Remove silent parameter from Timer class entirely - Remove silent parameter from timeit function - Update all logic to use only verbose parameter - Update all tests to use verbose=False instead of silent=True - Remove backward compatibility tests for silent parameter - Simplify API to have only one parameter controlling output behavior Co-authored-by: mmcky <[email protected]> * Update version to 0.10.1 for new release Co-authored-by: mmcky <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: mmcky <[email protected]>
1 parent c8489d0 commit 2628f50

File tree

3 files changed

+104
-48
lines changed

3 files changed

+104
-48
lines changed

quantecon/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Import the main names to top level.
44
"""
55

6-
__version__ = '0.10.0'
6+
__version__ = '0.10.1'
77

88
try:
99
import numba

quantecon/util/tests/test_timing.py

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def setup_method(self):
6464

6565
def test_basic_timer(self):
6666
"""Test basic Timer context manager functionality."""
67-
timer = Timer(silent=True)
67+
timer = Timer(verbose=False)
6868

6969
with timer:
7070
time.sleep(self.sleep_time)
@@ -75,7 +75,7 @@ def test_basic_timer(self):
7575

7676
def test_timer_return_value(self):
7777
"""Test that Timer returns self for variable assignment."""
78-
with Timer(silent=True) as timer:
78+
with Timer(verbose=False) as timer:
7979
time.sleep(self.sleep_time)
8080

8181
assert timer.elapsed is not None
@@ -84,17 +84,17 @@ def test_timer_return_value(self):
8484
def test_timer_units(self):
8585
"""Test different time units."""
8686
# Test seconds (default)
87-
with Timer(silent=True) as timer_sec:
87+
with Timer(verbose=False) as timer_sec:
8888
time.sleep(self.sleep_time)
8989
expected_sec = self.sleep_time
9090
assert_allclose(timer_sec.elapsed, expected_sec, atol=0.05, rtol=2)
9191

9292
# Timer always stores elapsed time in seconds regardless of display unit
93-
with Timer(unit="milliseconds", silent=True) as timer_ms:
93+
with Timer(unit="milliseconds", verbose=False) as timer_ms:
9494
time.sleep(self.sleep_time)
9595
assert_allclose(timer_ms.elapsed, expected_sec, atol=0.05, rtol=2)
9696

97-
with Timer(unit="microseconds", silent=True) as timer_us:
97+
with Timer(unit="microseconds", verbose=False) as timer_us:
9898
time.sleep(self.sleep_time)
9999
assert_allclose(timer_us.elapsed, expected_sec, atol=0.05, rtol=2)
100100

@@ -109,33 +109,33 @@ def test_invalid_unit(self):
109109
def test_timer_precision(self):
110110
"""Test that precision parameter is accepted (output format tested manually)."""
111111
# Just verify it doesn't crash with different precision values
112-
with Timer(precision=0, silent=True) as timer0:
112+
with Timer(precision=0, verbose=False) as timer0:
113113
time.sleep(self.sleep_time)
114-
with Timer(precision=6, silent=True) as timer6:
114+
with Timer(precision=6, verbose=False) as timer6:
115115
time.sleep(self.sleep_time)
116116

117117
assert timer0.elapsed is not None
118118
assert timer6.elapsed is not None
119119

120120
def test_timer_message(self):
121121
"""Test custom message parameter (output format tested manually)."""
122-
with Timer(message="Test operation", silent=True) as timer:
122+
with Timer(message="Test operation", verbose=False) as timer:
123123
time.sleep(self.sleep_time)
124124

125125
assert timer.elapsed is not None
126126

127-
def test_timer_silent_mode(self):
128-
"""Test silent mode suppresses output."""
129-
# This mainly tests that silent=True doesn't crash
127+
def test_timer_verbose_mode(self):
128+
"""Test verbose mode controls output."""
129+
# This mainly tests that verbose=False doesn't crash
130130
# Output suppression is hard to test automatically
131-
with Timer(silent=True) as timer:
131+
with Timer(verbose=False) as timer:
132132
time.sleep(self.sleep_time)
133133

134134
assert timer.elapsed is not None
135135

136136
def test_timer_exception_handling(self):
137137
"""Test that Timer works correctly even when exceptions occur."""
138-
timer = Timer(silent=True)
138+
timer = Timer(verbose=False)
139139

140140
try:
141141
with timer:
@@ -153,7 +153,7 @@ def test_timeit_basic(self):
153153
def test_func():
154154
time.sleep(self.sleep_time)
155155

156-
result = timeit(test_func, runs=3, silent=True)
156+
result = timeit(test_func, runs=3, verbose=False, results=True)
157157

158158
# Check that we have results
159159
assert 'elapsed' in result
@@ -177,7 +177,7 @@ def test_func_with_args(sleep_time, multiplier=1):
177177

178178
# Use lambda to bind arguments
179179
func_with_args = lambda: test_func_with_args(self.sleep_time, 0.5)
180-
result = timeit(func_with_args, runs=2, silent=True)
180+
result = timeit(func_with_args, runs=2, verbose=False, results=True)
181181

182182
# Check results
183183
assert len(result['elapsed']) == 2
@@ -214,7 +214,7 @@ def test_timeit_single_run(self):
214214
def test_func():
215215
time.sleep(self.sleep_time)
216216

217-
result = timeit(test_func, runs=1, silent=True)
217+
result = timeit(test_func, runs=1, verbose=False, results=True)
218218

219219
assert len(result['elapsed']) == 1
220220
assert result['average'] == result['elapsed'][0]
@@ -226,12 +226,12 @@ def test_timeit_different_units(self):
226226
def test_func():
227227
time.sleep(self.sleep_time)
228228

229-
# Test milliseconds (silent mode to avoid output during tests)
230-
result_ms = timeit(test_func, runs=2, unit="milliseconds", silent=True)
229+
# Test milliseconds (verbose=False to avoid output during tests)
230+
result_ms = timeit(test_func, runs=2, unit="milliseconds", verbose=False, results=True)
231231
assert len(result_ms['elapsed']) == 2
232232

233233
# Test microseconds
234-
result_us = timeit(test_func, runs=2, unit="microseconds", silent=True)
234+
result_us = timeit(test_func, runs=2, unit="microseconds", verbose=False, results=True)
235235
assert len(result_us['elapsed']) == 2
236236

237237
# All results should be in seconds regardless of display unit
@@ -246,7 +246,7 @@ def test_func():
246246
time.sleep(self.sleep_time)
247247

248248
# This test is mainly to ensure stats_only doesn't crash
249-
result = timeit(test_func, runs=2, stats_only=True, silent=True)
249+
result = timeit(test_func, runs=2, stats_only=True, verbose=False, results=True)
250250
assert len(result['elapsed']) == 2
251251

252252
def test_timeit_invalid_timer_kwargs(self):
@@ -260,3 +260,34 @@ def test_func():
260260
except ValueError as e:
261261
assert "Unknown timer parameters" in str(e)
262262

263+
def test_timeit_verbose_parameter(self):
264+
"""Test verbose parameter controls output."""
265+
def test_func():
266+
time.sleep(self.sleep_time)
267+
268+
# Test verbose=True (default) - this test can't easily verify output
269+
# but at least checks it doesn't crash
270+
result1 = timeit(test_func, runs=2, results=True)
271+
assert result1 is not None
272+
assert len(result1['elapsed']) == 2
273+
274+
# Test verbose=False - should suppress output
275+
result2 = timeit(test_func, runs=2, verbose=False, results=True)
276+
assert result2 is not None
277+
assert len(result2['elapsed']) == 2
278+
279+
def test_timeit_results_parameter(self):
280+
"""Test results parameter controls return value."""
281+
def test_func():
282+
time.sleep(self.sleep_time)
283+
284+
# Test results=False (default) - should return None
285+
result1 = timeit(test_func, runs=2, verbose=False)
286+
assert result1 is None
287+
288+
# Test results=True - should return timing data
289+
result2 = timeit(test_func, runs=2, verbose=False, results=True)
290+
assert result2 is not None
291+
assert 'elapsed' in result2
292+
assert len(result2['elapsed']) == 2
293+

quantecon/util/timing.py

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ class Timer:
194194
Number of decimal places to display for seconds.
195195
unit : str, optional(default="seconds")
196196
Unit to display timing in. Options: "seconds", "milliseconds", "microseconds"
197-
silent : bool, optional(default=False)
198-
If True, suppress printing of timing results.
197+
verbose : bool, optional(default=True)
198+
If True, print timing results. If False, suppress printing of timing results.
199199
200200
Attributes
201201
----------
@@ -217,19 +217,19 @@ class Timer:
217217
Computing results: 0.0001 seconds elapsed
218218
219219
Store elapsed time for comparison:
220-
>>> timer = Timer(silent=True)
220+
>>> timer = Timer(verbose=False)
221221
>>> with timer:
222222
... # some code
223223
... pass
224224
>>> print(f"Method took {timer.elapsed:.6f} seconds")
225225
Method took 0.000123 seconds
226226
"""
227227

228-
def __init__(self, message="", precision=2, unit="seconds", silent=False):
228+
def __init__(self, message="", precision=2, unit="seconds", verbose=True):
229229
self.message = message
230230
self.precision = precision
231231
self.unit = unit.lower()
232-
self.silent = silent
232+
self.verbose = verbose
233233
self.elapsed = None
234234
self._start_time = None
235235

@@ -246,7 +246,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
246246
end_time = time.time()
247247
self.elapsed = end_time - self._start_time
248248

249-
if not self.silent:
249+
if self.verbose:
250250
self._print_elapsed()
251251

252252
def _print_elapsed(self):
@@ -271,7 +271,7 @@ def _print_elapsed(self):
271271
print(f"{prefix}{elapsed_display:.{self.precision}f} {unit_str} elapsed")
272272

273273

274-
def timeit(func, runs=1, stats_only=False, **timer_kwargs):
274+
def timeit(func, runs=1, stats_only=False, verbose=True, results=False, **timer_kwargs):
275275
"""
276276
Execute a function multiple times and collect timing statistics.
277277
@@ -288,18 +288,23 @@ def timeit(func, runs=1, stats_only=False, **timer_kwargs):
288288
stats_only : bool, optional(default=False)
289289
If True, only display summary statistics. If False, display
290290
individual run times followed by summary statistics.
291+
verbose : bool, optional(default=True)
292+
If True, print nicely formatted timing output all at once at the end.
293+
If False, suppress all output.
294+
results : bool, optional(default=False)
295+
If True, return dictionary with timing results. If False, return None.
291296
**timer_kwargs
292-
Keyword arguments to pass to Timer (message, precision, unit, silent).
293-
Note: silent parameter controls all output when stats_only=False.
297+
Keyword arguments to pass to Timer (message, precision, unit, verbose).
294298
295299
Returns
296300
-------
297-
dict
298-
Dictionary containing timing results with keys:
301+
dict or None
302+
If results=True, returns dictionary containing timing results with keys:
299303
- 'elapsed': list of elapsed times for each run
300304
- 'average': average elapsed time
301305
- 'minimum': minimum elapsed time
302306
- 'maximum': maximum elapsed time
307+
If results=False, returns None.
303308
304309
Examples
305310
--------
@@ -322,6 +327,13 @@ def timeit(func, runs=1, stats_only=False, **timer_kwargs):
322327
Run 2: 10.0 ms
323328
Average: 10.1 ms, Minimum: 10.0 ms, Maximum: 10.1 ms
324329
330+
Return results for further analysis:
331+
>>> results = timeit(slow_function, runs=2, results=True)
332+
>>> print(f"Average time: {results['average']:.4f} seconds")
333+
334+
Quiet mode:
335+
>>> timeit(slow_function, runs=2, verbose=False) # No output
336+
325337
With function arguments using lambda:
326338
>>> add_func = lambda: expensive_computation(5, 10)
327339
>>> timeit(add_func, runs=2)
@@ -337,27 +349,31 @@ def timeit(func, runs=1, stats_only=False, **timer_kwargs):
337349
'message': timer_kwargs.pop('message', ''),
338350
'precision': timer_kwargs.pop('precision', 2),
339351
'unit': timer_kwargs.pop('unit', 'seconds'),
340-
'silent': timer_kwargs.pop('silent', False) # Explicit silent parameter
352+
'verbose': timer_kwargs.pop('verbose', True) # Timer verbose parameter
341353
}
342354

343355
# Warn about unused kwargs
344356
if timer_kwargs:
345357
raise ValueError(f"Unknown timer parameters: {list(timer_kwargs.keys())}")
346358

359+
# Determine if we should show output
360+
show_output = verbose
361+
347362
run_times = []
363+
output_lines = [] # Collect output lines for printing all at once
348364

349365
# Execute the function multiple times
350366
for i in range(runs):
351367
# Always silence individual Timer output to avoid duplication with our run display
352368
individual_timer_params = timer_params.copy()
353-
individual_timer_params['silent'] = True
369+
individual_timer_params['verbose'] = False
354370

355371
with Timer(**individual_timer_params) as timer:
356372
func()
357373
run_times.append(timer.elapsed)
358374

359-
# Print individual run times unless stats_only or silent
360-
if not stats_only and not timer_params['silent']:
375+
# Collect individual run output lines (but don't print yet)
376+
if show_output and not stats_only:
361377
# Convert to requested unit for display
362378
unit = timer_params['unit'].lower()
363379
precision = timer_params['precision']
@@ -372,15 +388,15 @@ def timeit(func, runs=1, stats_only=False, **timer_kwargs):
372388
elapsed_display = timer.elapsed
373389
unit_str = "seconds"
374390

375-
print(f"Run {i + 1}: {elapsed_display:.{precision}f} {unit_str}")
391+
output_lines.append(f"Run {i + 1}: {elapsed_display:.{precision}f} {unit_str}")
376392

377393
# Calculate statistics
378394
average = sum(run_times) / len(run_times)
379395
minimum = min(run_times)
380396
maximum = max(run_times)
381397

382-
# Print summary statistics unless completely silent
383-
if not timer_params['silent']:
398+
# Collect summary statistics output line (but don't print yet)
399+
if show_output:
384400
# Convert to requested unit for display
385401
unit = timer_params['unit'].lower()
386402
precision = timer_params['precision']
@@ -401,16 +417,25 @@ def timeit(func, runs=1, stats_only=False, **timer_kwargs):
401417
max_display = maximum
402418
unit_str = "seconds"
403419

404-
print(f"Average: {avg_display:.{precision}f} {unit_str}, "
405-
f"Minimum: {min_display:.{precision}f} {unit_str}, "
406-
f"Maximum: {max_display:.{precision}f} {unit_str}")
420+
summary_line = (f"Average: {avg_display:.{precision}f} {unit_str}, "
421+
f"Minimum: {min_display:.{precision}f} {unit_str}, "
422+
f"Maximum: {max_display:.{precision}f} {unit_str}")
423+
output_lines.append(summary_line)
407424

408-
return {
409-
'elapsed': run_times,
410-
'average': average,
411-
'minimum': minimum,
412-
'maximum': maximum
413-
}
425+
# Print all output lines at once
426+
if show_output and output_lines:
427+
print('\n'.join(output_lines))
428+
429+
# Return results only if requested
430+
if results:
431+
return {
432+
'elapsed': run_times,
433+
'average': average,
434+
'minimum': minimum,
435+
'maximum': maximum
436+
}
437+
else:
438+
return None
414439

415440

416441
def tic():

0 commit comments

Comments
 (0)