From 3ae528451e1b7b48a1fc8e76cfc6bc3d7a94a044 Mon Sep 17 00:00:00 2001 From: Mario Corchero Date: Sun, 7 Jan 2018 16:45:31 +0000 Subject: [PATCH 1/2] Add -m option to profile for profiling modules 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`. --- Lib/profile.py | 32 ++++++++++++------- .../2018-01-07-17-43-10.bpo-32512.flC-dE.rst | 2 ++ 2 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-01-07-17-43-10.bpo-32512.flC-dE.rst diff --git a/Lib/profile.py b/Lib/profile.py index 0340a7907bfd479..5df43604acddb21 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/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 000000000000000..0a7763daffad922 --- /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. From 60a37c77ab075cba8d67de49768deb0a26e5f907 Mon Sep 17 00:00:00 2001 From: Mario Corchero Date: Sun, 7 Jan 2018 19:06:10 +0000 Subject: [PATCH 2/2] Move cProfile test for -m to run on both modules 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/test/test_cprofile.py | 13 ------------- Lib/test/test_profile.py | 13 +++++++++++++ 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 1a772fb7f9414be..9ceb81603c951ec 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/test/test_cprofile.py b/Lib/test/test_cprofile.py index 406d70305f9e9c8..efcf6bc9280372f 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -37,19 +37,6 @@ def test_bad_counter_during_dealloc(self): 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 a9982663175a760..01a8a6eaf5a23cc 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -11,6 +11,7 @@ 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 @@ def test_runctx(self): 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')