Skip to content

Commit 22cff50

Browse files
committed
Test for --eager-preimports
tests/test_autoprofile.py test_autoprofile_eager_preimports() New test for the behaviors of the `-e` and `-p` options test_autoprofile_callable_wrapper_objects() Test that on-import autoprofiling catches callable wrapper types like classmethods
1 parent 9771eca commit 22cff50

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

tests/test_autoprofile.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,150 @@ def test_autoprofile_exec_module(
525525
assert ('Function: add_four' in raw_output) == add_four
526526
assert ('Function: add_operator' in raw_output) == add_operator
527527
assert ('Function: _main' in raw_output) == main
528+
529+
530+
@pytest.mark.parametrize(
531+
['prof_mod', 'eager_preimports',
532+
'add_one', 'add_two', 'add_three', 'add_four', 'add_operator', 'main'],
533+
# Test that `--eager-preimports` know to exclude the script run
534+
# (so as not to inadvertantly run it twice)
535+
[('script.py', None, False, False, False, False, False, True),
536+
(None, 'script.py', False, False, False, False, False, True),
537+
# Test explicitly passing targets to `--eager-preimports`
538+
(['test_mod.submod1,test_mod.submod2', 'test_mod.subpkg.submod4'], None,
539+
True, True, False, False, True, False),
540+
(['test_mod.submod1,test_mod.submod2'], ['test_mod.subpkg.submod4'],
541+
True, True, False, True, True, False),
542+
(None, ['test_mod.submod1,test_mod.submod2', 'test_mod.subpkg.submod4'],
543+
True, True, False, True, True, False),
544+
# Test implicitly passing targets to `--eager-preimports`
545+
(['test_mod.submod1,test_mod.submod2', 'test_mod.subpkg.submod4'], True,
546+
True, True, False, True, True, False)])
547+
def test_autoprofile_eager_preimports(
548+
prof_mod, eager_preimports,
549+
add_one, add_two, add_three, add_four, add_operator, main):
550+
"""
551+
Test eager imports with the `-e`/`--eager-preimports` flag.
552+
"""
553+
with tempfile.TemporaryDirectory() as tmpdir:
554+
temp_dpath = ub.Path(tmpdir)
555+
_write_demo_module(temp_dpath)
556+
557+
args = [sys.executable, '-m', 'kernprof']
558+
if prof_mod is not None:
559+
if isinstance(prof_mod, str):
560+
prof_mod = [prof_mod]
561+
for target in prof_mod:
562+
args.extend(['-p', target])
563+
if eager_preimports in (True,):
564+
args.append('-e')
565+
elif eager_preimports is not None:
566+
if isinstance(eager_preimports, str):
567+
eager_preimports = [eager_preimports]
568+
for target in eager_preimports:
569+
args.extend(['-e', target])
570+
args.extend(['-l', 'script.py'])
571+
proc = ub.cmd(args, cwd=temp_dpath, verbose=2)
572+
# Check that pre-imports don't accidentally run the code twice
573+
assert proc.stdout.count('7.9') == 1
574+
print(proc.stdout)
575+
print(proc.stderr)
576+
proc.check_returncode()
577+
578+
prof = temp_dpath / 'script.py.lprof'
579+
580+
args = [sys.executable, '-m', 'line_profiler', os.fspath(prof)]
581+
proc = ub.cmd(args, cwd=temp_dpath)
582+
raw_output = proc.stdout
583+
print(raw_output)
584+
proc.check_returncode()
585+
586+
assert ('Function: add_one' in raw_output) == add_one
587+
assert ('Function: add_two' in raw_output) == add_two
588+
assert ('Function: add_three' in raw_output) == add_three
589+
assert ('Function: add_four' in raw_output) == add_four
590+
assert ('Function: add_operator' in raw_output) == add_operator
591+
assert ('Function: main' in raw_output) == main
592+
593+
594+
@pytest.mark.parametrize(
595+
('eager_preimports, function, method, class_method, static_method, '
596+
'descriptor'),
597+
[('my_module', True, True, True, True, True),
598+
# `function()` included in profiling via `Class.partial_method()`
599+
('my_module.Class', True, True, True, True, True),
600+
('my_module.Class.descriptor', False, False, False, False, True)])
601+
def test_autoprofile_callable_wrapper_objects(
602+
eager_preimports, function, method, class_method, static_method,
603+
descriptor):
604+
"""
605+
Test that on-import profiling catches various callable-wrapper
606+
object types:
607+
- properties
608+
- staticmethod
609+
- classmethod
610+
- partialmethod
611+
Like it does regular methods and functions.
612+
"""
613+
with tempfile.TemporaryDirectory() as tmpdir:
614+
temp_dpath = ub.Path(tmpdir)
615+
path = temp_dpath / 'path'
616+
path.mkdir()
617+
(path / 'my_module.py').write_text(ub.codeblock("""
618+
import functools
619+
620+
621+
def function(x):
622+
return
623+
624+
625+
class Class:
626+
def method(self):
627+
return
628+
629+
@classmethod
630+
def class_method(cls):
631+
return
632+
633+
@staticmethod
634+
def static_method():
635+
return
636+
637+
partial_method = functools.partial(function)
638+
639+
@property
640+
def descriptor(self):
641+
return
642+
"""))
643+
(temp_dpath / 'script.py').write_text(ub.codeblock("""
644+
import my_module
645+
646+
647+
if __name__ == '__main__':
648+
pass
649+
"""))
650+
651+
with ub.ChDir(temp_dpath):
652+
args = [sys.executable, '-m', 'kernprof',
653+
'-e', eager_preimports, '-lv', 'script.py']
654+
python_path = os.environ.get('PYTHONPATH')
655+
if python_path:
656+
python_path = '{}:{}'.format(path, python_path)
657+
else:
658+
python_path = str(path)
659+
proc = ub.cmd(args,
660+
env={**os.environ, 'PYTHONPATH': python_path},
661+
verbose=2)
662+
raw_output = proc.stdout
663+
print(raw_output)
664+
print(proc.stderr)
665+
proc.check_returncode()
666+
667+
assert ('Function: function' in raw_output) == function
668+
assert ('Function: method' in raw_output) == method
669+
assert ('Function: class_method' in raw_output) == class_method
670+
assert ('Function: static_method' in raw_output) == static_method
671+
# `partial_method()` not included as its own item because it's a
672+
# wrapper around `function()`
673+
assert 'Function: partial_method' not in raw_output
674+
assert ('Function: descriptor' in raw_output) == descriptor

0 commit comments

Comments
 (0)