From 354227a1e90036d8c1481a211746de912c6c7c33 Mon Sep 17 00:00:00 2001 From: Mario Corchero Date: Sat, 1 Jun 2019 05:49:10 +0100 Subject: Add option to trace to run modules (GH-5134) Adds a new option in trace that allows tracing runnable modules. It is exposed as `--module module_name` as `-m` is already in use for another argument. --- Doc/library/trace.rst | 3 ++ Lib/test/test_trace.py | 7 +++- Lib/trace.py | 46 +++++++++++++++------- .../2018-01-07-21-04-50.bpo-32515.D8_Wcb.rst | 1 + 4 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-01-07-21-04-50.bpo-32515.D8_Wcb.rst diff --git a/Doc/library/trace.rst b/Doc/library/trace.rst index 5cb7029..85fec68 100644 --- a/Doc/library/trace.rst +++ b/Doc/library/trace.rst @@ -42,6 +42,9 @@ all Python modules imported during the execution into the current directory. Display the version of the module and exit. +.. versionadded:: 3.8 + Added ``--module`` option that allows to run an executable module. + Main options ^^^^^^^^^^^^ diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index afe7902..4bc21ea 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -474,7 +474,7 @@ class TestCommandLine(unittest.TestCase): def test_failures(self): _errors = ( - (b'filename is missing: required with the main options', '-l', '-T'), + (b'progname is missing: required with the main options', '-l', '-T'), (b'cannot specify both --listfuncs and (--trace or --count)', '-lc'), (b'argument -R/--no-report: not allowed with argument -r/--report', '-rR'), (b'must specify one of --trace, --count, --report, --listfuncs, or --trackcalls', '-g'), @@ -524,5 +524,10 @@ class TestCommandLine(unittest.TestCase): self.assertIn('lines cov% module (path)', stdout) self.assertIn(f'6 100% {TESTFN} ({filename})', stdout) + def test_run_as_module(self): + assert_python_ok('-m', 'trace', '-l', '--module', 'timeit', '-n', '1') + assert_python_failure('-m', 'trace', '-l', '--module', 'not_a_module_zzz') + + if __name__ == '__main__': unittest.main() diff --git a/Lib/trace.py b/Lib/trace.py index 63008a1..62325d3 100755 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -666,7 +666,9 @@ def main(): help='Ignore files in the given directory ' '(multiple directories can be joined by os.pathsep).') - parser.add_argument('filename', nargs='?', + parser.add_argument('--module', action='store_true', default=False, + help='Trace a module. ') + parser.add_argument('progname', nargs='?', help='file to run as main program') parser.add_argument('arguments', nargs=argparse.REMAINDER, help='arguments to the program') @@ -704,26 +706,40 @@ def main(): if opts.summary and not opts.count: parser.error('--summary can only be used with --count or --report') - if opts.filename is None: - parser.error('filename is missing: required with the main options') - - sys.argv = [opts.filename, *opts.arguments] - sys.path[0] = os.path.dirname(opts.filename) + if opts.progname is None: + parser.error('progname is missing: required with the main options') t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs, countcallers=opts.trackcalls, ignoremods=opts.ignore_module, ignoredirs=opts.ignore_dir, infile=opts.file, outfile=opts.file, timing=opts.timing) try: - with open(opts.filename) as fp: - code = compile(fp.read(), opts.filename, 'exec') - # try to emulate __main__ namespace as much as possible - globs = { - '__file__': opts.filename, - '__name__': '__main__', - '__package__': None, - '__cached__': None, - } + if opts.module: + import runpy + module_name = opts.progname + mod_name, mod_spec, code = runpy._get_module_details(module_name) + sys.argv = [code.co_filename, *opts.arguments] + globs = { + '__name__': '__main__', + '__file__': code.co_filename, + '__package__': mod_spec.parent, + '__loader__': mod_spec.loader, + '__spec__': mod_spec, + '__cached__': None, + } + else: + sys.argv = [opts.progname, *opts.arguments] + sys.path[0] = os.path.dirname(opts.progname) + + with open(opts.progname) as fp: + code = compile(fp.read(), opts.progname, 'exec') + # try to emulate __main__ namespace as much as possible + globs = { + '__file__': opts.progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } t.runctx(code, globs, globs) except OSError as err: sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err)) diff --git a/Misc/NEWS.d/next/Library/2018-01-07-21-04-50.bpo-32515.D8_Wcb.rst b/Misc/NEWS.d/next/Library/2018-01-07-21-04-50.bpo-32515.D8_Wcb.rst new file mode 100644 index 0000000..ad585b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-01-07-21-04-50.bpo-32515.D8_Wcb.rst @@ -0,0 +1 @@ +trace.py can now run modules via python3 -m trace -t --module module_name -- cgit v0.12