|
| 1 | +import contextlib |
1 | 2 | import os |
2 | 3 | import re |
3 | 4 | import sys |
@@ -407,6 +408,96 @@ def func4(a): |
407 | 408 | temp_dpath.delete() |
408 | 409 |
|
409 | 410 |
|
| 411 | +@pytest.mark.parametrize('reset_enable_count', [True, False]) |
| 412 | +@pytest.mark.parametrize('wrap_class, wrap_module', |
| 413 | + [(None, None), (False, True), |
| 414 | + (True, False), (True, True)]) |
| 415 | +def test_profiler_add_methods(wrap_class, wrap_module, reset_enable_count): |
| 416 | + """ |
| 417 | + Test the `wrap` argument for the |
| 418 | + `line_profiler.autoprofile.autoprofile. |
| 419 | + _extend_line_profiler_for_profiling_imports()`, |
| 420 | + `LineProfiler.add_class()` and `.add_module()` methods. |
| 421 | + """ |
| 422 | + def write(path, code): |
| 423 | + path.write_text(ub.codeblock(code)) |
| 424 | + |
| 425 | + script = ub.codeblock(''' |
| 426 | + from line_profiler import LineProfiler |
| 427 | + from line_profiler.autoprofile.autoprofile import ( |
| 428 | + _extend_line_profiler_for_profiling_imports as upgrade_profiler) |
| 429 | +
|
| 430 | + import my_module_1 |
| 431 | + from my_module_2 import Class |
| 432 | + from my_module_3 import func3 |
| 433 | +
|
| 434 | +
|
| 435 | + profiler = LineProfiler() |
| 436 | + upgrade_profiler(profiler) |
| 437 | + profiler.add_imported_function_or_module(my_module_1{}) |
| 438 | + profiler.add_imported_function_or_module(Class{}) |
| 439 | + profiler.add_imported_function_or_module(func3) |
| 440 | +
|
| 441 | + if {}: |
| 442 | + for _ in range(profiler.enable_count): |
| 443 | + profiler.disable_by_count() |
| 444 | +
|
| 445 | + # `func1()` should only have timing info if `wrap_module` |
| 446 | + my_module_1.func1() |
| 447 | + # `method2()` should only have timing info if `wrap_class` |
| 448 | + Class.method2() |
| 449 | + # `func3()` is profiled but don't see any timing info because it |
| 450 | + # isn't wrapped and doesn't auto-`.enable()` before being called |
| 451 | + func3() |
| 452 | + profiler.print_stats(details=True, summarize=True) |
| 453 | + '''.format( |
| 454 | + '' if wrap_module is None else f', wrap={wrap_module}', |
| 455 | + '' if wrap_class is None else f', wrap={wrap_class}', |
| 456 | + reset_enable_count)) |
| 457 | + |
| 458 | + with contextlib.ExitStack() as stack: |
| 459 | + enter = stack.enter_context |
| 460 | + enter(ub.ChDir(enter(tempfile.TemporaryDirectory()))) |
| 461 | + curdir = ub.Path.cwd() |
| 462 | + write(curdir / 'script.py', script) |
| 463 | + write(curdir / 'my_module_1.py', |
| 464 | + ''' |
| 465 | + def func1(): |
| 466 | + pass # Marker: func1 |
| 467 | + ''') |
| 468 | + write(curdir / 'my_module_2.py', |
| 469 | + ''' |
| 470 | + class Class: |
| 471 | + @classmethod |
| 472 | + def method2(cls): |
| 473 | + pass # Marker: method2 |
| 474 | + ''') |
| 475 | + write(curdir / 'my_module_3.py', |
| 476 | + ''' |
| 477 | + def func3(): |
| 478 | + pass # Marker: func3 |
| 479 | + ''') |
| 480 | + proc = ub.cmd([sys.executable, str(curdir / 'script.py')]) |
| 481 | + |
| 482 | + # Check that the profiler has seen each of the methods |
| 483 | + raw_output = proc.stdout |
| 484 | + print(script) |
| 485 | + print(raw_output) |
| 486 | + print(proc.stderr) |
| 487 | + proc.check_returncode() |
| 488 | + assert '# Marker: func1' in raw_output |
| 489 | + assert '# Marker: method2' in raw_output |
| 490 | + assert '# Marker: func3' in raw_output |
| 491 | + |
| 492 | + # Check that the timing info (of the lack thereof) are correct |
| 493 | + for func, has_timing in [('func1', wrap_module), ('method2', wrap_class), |
| 494 | + ('func3', False)]: |
| 495 | + line, = (line for line in raw_output.splitlines() |
| 496 | + if line.endswith('Marker: ' + func)) |
| 497 | + has_timing = has_timing or not reset_enable_count |
| 498 | + assert line.split()[1] == ('1' if has_timing else 'pass') |
| 499 | + |
| 500 | + |
410 | 501 | if __name__ == '__main__': |
411 | 502 | ... |
412 | 503 | test_simple_explicit_nonglobal_usage() |
0 commit comments