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.
This commit is contained in:
parent
664fe3996f
commit
354227a1e9
|
@ -42,6 +42,9 @@ all Python modules imported during the execution into the current directory.
|
||||||
|
|
||||||
Display the version of the module and exit.
|
Display the version of the module and exit.
|
||||||
|
|
||||||
|
.. versionadded:: 3.8
|
||||||
|
Added ``--module`` option that allows to run an executable module.
|
||||||
|
|
||||||
Main options
|
Main options
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -474,7 +474,7 @@ class TestCommandLine(unittest.TestCase):
|
||||||
|
|
||||||
def test_failures(self):
|
def test_failures(self):
|
||||||
_errors = (
|
_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'cannot specify both --listfuncs and (--trace or --count)', '-lc'),
|
||||||
(b'argument -R/--no-report: not allowed with argument -r/--report', '-rR'),
|
(b'argument -R/--no-report: not allowed with argument -r/--report', '-rR'),
|
||||||
(b'must specify one of --trace, --count, --report, --listfuncs, or --trackcalls', '-g'),
|
(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('lines cov% module (path)', stdout)
|
||||||
self.assertIn(f'6 100% {TESTFN} ({filename})', 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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
46
Lib/trace.py
46
Lib/trace.py
|
@ -666,7 +666,9 @@ def main():
|
||||||
help='Ignore files in the given directory '
|
help='Ignore files in the given directory '
|
||||||
'(multiple directories can be joined by os.pathsep).')
|
'(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')
|
help='file to run as main program')
|
||||||
parser.add_argument('arguments', nargs=argparse.REMAINDER,
|
parser.add_argument('arguments', nargs=argparse.REMAINDER,
|
||||||
help='arguments to the program')
|
help='arguments to the program')
|
||||||
|
@ -704,26 +706,40 @@ def main():
|
||||||
if opts.summary and not opts.count:
|
if opts.summary and not opts.count:
|
||||||
parser.error('--summary can only be used with --count or --report')
|
parser.error('--summary can only be used with --count or --report')
|
||||||
|
|
||||||
if opts.filename is None:
|
if opts.progname is None:
|
||||||
parser.error('filename is missing: required with the main options')
|
parser.error('progname is missing: required with the main options')
|
||||||
|
|
||||||
sys.argv = [opts.filename, *opts.arguments]
|
|
||||||
sys.path[0] = os.path.dirname(opts.filename)
|
|
||||||
|
|
||||||
t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs,
|
t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs,
|
||||||
countcallers=opts.trackcalls, ignoremods=opts.ignore_module,
|
countcallers=opts.trackcalls, ignoremods=opts.ignore_module,
|
||||||
ignoredirs=opts.ignore_dir, infile=opts.file,
|
ignoredirs=opts.ignore_dir, infile=opts.file,
|
||||||
outfile=opts.file, timing=opts.timing)
|
outfile=opts.file, timing=opts.timing)
|
||||||
try:
|
try:
|
||||||
with open(opts.filename) as fp:
|
if opts.module:
|
||||||
code = compile(fp.read(), opts.filename, 'exec')
|
import runpy
|
||||||
# try to emulate __main__ namespace as much as possible
|
module_name = opts.progname
|
||||||
globs = {
|
mod_name, mod_spec, code = runpy._get_module_details(module_name)
|
||||||
'__file__': opts.filename,
|
sys.argv = [code.co_filename, *opts.arguments]
|
||||||
'__name__': '__main__',
|
globs = {
|
||||||
'__package__': None,
|
'__name__': '__main__',
|
||||||
'__cached__': None,
|
'__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)
|
t.runctx(code, globs, globs)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err))
|
sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err))
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
trace.py can now run modules via python3 -m trace -t --module module_name
|
Loading…
Reference in New Issue