From ad1a25f499362eaf9cbfcafa0b8e2454eb43dcf1 Mon Sep 17 00:00:00 2001 From: Mario Corchero Date: Mon, 5 Nov 2018 15:03:46 +0300 Subject: bpo-32512: Add -m option to profile for profiling modules (#5132) The new option in the CLI of the profile module allow to profile executable modules. This change follows the same implementation as the one already present in `cProfile`. As the argument is now present on both modules, move the tests to the common test case to be run with profile as well. --- Doc/library/profile.rst | 9 ++++-- Lib/profile.py | 32 ++++++++++++++-------- Lib/test/test_cprofile.py | 13 --------- Lib/test/test_profile.py | 13 +++++++++ .../2018-01-07-17-43-10.bpo-32512.flC-dE.rst | 2 ++ 5 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-01-07-17-43-10.bpo-32512.flC-dE.rst diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 1a772fb..9ceb816 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -120,8 +120,8 @@ results to a file by specifying a filename to the :func:`run` function:: The :class:`pstats.Stats` class reads profile results from a file and formats them in various ways. -The file :mod:`cProfile` can also be invoked as a script to profile another -script. For example:: +The files :mod:`cProfile` and :mod:`profile` can also be invoked as a script to +profile another script. For example:: python -m cProfile [-o output_file] [-s sort_order] (-m module | myscript.py) @@ -133,7 +133,10 @@ the output by. This only applies when ``-o`` is not supplied. ``-m`` specifies that a module is being profiled instead of a script. .. versionadded:: 3.7 - Added the ``-m`` option. + Added the ``-m`` option to :mod:`cProfile`. + + .. versionadded:: 3.8 + Added the ``-m`` option to :mod:`profile`. The :mod:`pstats` module's :class:`~pstats.Stats` class has a variety of methods for manipulating and printing the data saved into a profile results file:: diff --git a/Lib/profile.py b/Lib/profile.py index 0340a79..5df4360 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -553,11 +553,13 @@ def main(): import os from optparse import OptionParser - usage = "profile.py [-o output_file_path] [-s sort] scriptfile [arg] ..." + usage = "profile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." parser = OptionParser(usage=usage) parser.allow_interspersed_args = False parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) + parser.add_option('-m', dest="module", action="store_true", + help="Profile a library module.", default=False) parser.add_option('-s', '--sort', dest="sort", help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) @@ -570,16 +572,24 @@ def main(): sys.argv[:] = args if len(args) > 0: - progname = args[0] - sys.path.insert(0, os.path.dirname(progname)) - with open(progname, 'rb') as fp: - code = compile(fp.read(), progname, 'exec') - globs = { - '__file__': progname, - '__name__': '__main__', - '__package__': None, - '__cached__': None, - } + if options.module: + import runpy + code = "run_module(modname, run_name='__main__')" + globs = { + 'run_module': runpy.run_module, + 'modname': args[0] + } + else: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with open(progname, 'rb') as fp: + code = compile(fp.read(), progname, 'exec') + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } runctx(code, globs, None, options.outfile, options.sort) else: parser.print_usage() diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py index 406d703..efcf6bc 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -37,19 +37,6 @@ class CProfileTest(ProfileTest): finally: unlink(TESTFN) - # Issue 21862 - def test_module_path_option(self): - # Test -m switch with modules - - # Test that -m switch needs an argument - assert_python_failure('-m', 'cProfile', '-m') - - # Test failure for not-existent module - assert_python_failure('-m', 'cProfile', '-m', 'random_module_xyz') - - # Test successful run - assert_python_ok('-m', 'cProfile', '-m', 'timeit', '-n', '1') - def test_profile_enable_disable(self): prof = self.profilerclass() # Make sure we clean ourselves up if the test fails for some reason. diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index a998266..01a8a6e 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -11,6 +11,7 @@ from contextlib import contextmanager import profile from test.profilee import testfunc, timer +from test.support.script_helper import assert_python_failure, assert_python_ok class ProfileTest(unittest.TestCase): @@ -98,6 +99,18 @@ class ProfileTest(unittest.TestCase): filename=TESTFN) self.assertTrue(os.path.exists(TESTFN)) + def test_run_profile_as_module(self): + # Test that -m switch needs an argument + assert_python_failure('-m', self.profilermodule.__name__, '-m') + + # Test failure for not-existent module + assert_python_failure('-m', self.profilermodule.__name__, + '-m', 'random_module_xyz') + + # Test successful run + assert_python_ok('-m', self.profilermodule.__name__, + '-m', 'timeit', '-n', '1') + def regenerate_expected_output(filename, cls): filename = filename.rstrip('co') diff --git a/Misc/NEWS.d/next/Library/2018-01-07-17-43-10.bpo-32512.flC-dE.rst b/Misc/NEWS.d/next/Library/2018-01-07-17-43-10.bpo-32512.flC-dE.rst new file mode 100644 index 0000000..0a7763d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-01-07-17-43-10.bpo-32512.flC-dE.rst @@ -0,0 +1,2 @@ +:mod:`profile` CLI accepts `-m module_name` as an alternative to +script path. -- cgit v0.12