Update trace.py from 3.13.5 (#6029)

This commit is contained in:
Shahar Naveh
2025-07-25 03:50:41 +02:00
committed by GitHub
parent 8621b3d7da
commit 6a9579efc7
2 changed files with 71 additions and 23 deletions

View File

@@ -1,11 +1,12 @@
import os
from pickle import dump
import sys
from test.support import captured_stdout
from test.support import captured_stdout, requires_resource, requires_gil_enabled
from test.support.os_helper import (TESTFN, rmtree, unlink)
from test.support.script_helper import assert_python_ok, assert_python_failure
import textwrap
import unittest
from types import FunctionType
import trace
from trace import Trace
@@ -197,9 +198,7 @@ class TestLineCounts(unittest.TestCase):
firstlineno_called = get_firstlineno(traced_doubler)
expected = {
(self.my_py_filename, firstlineno_calling + 1): 1,
# List comprehensions work differently in 3.x, so the count
# below changed compared to 2.x.
(self.my_py_filename, firstlineno_calling + 2): 12,
(self.my_py_filename, firstlineno_calling + 2): 11,
(self.my_py_filename, firstlineno_calling + 3): 1,
(self.my_py_filename, firstlineno_called + 1): 10,
}
@@ -251,7 +250,7 @@ class TestRunExecCounts(unittest.TestCase):
self.my_py_filename = fix_ext_py(__file__)
self.addCleanup(sys.settrace, sys.gettrace())
# TODO: RUSTPYTHON, KeyError: ('Lib/test/test_trace.py', 43)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_exec_counts(self):
self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
@@ -287,7 +286,7 @@ class TestFuncs(unittest.TestCase):
if self._saved_tracefunc is not None:
sys.settrace(self._saved_tracefunc)
# TODO: RUSTPYTHON, gc
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_simple_caller(self):
self.tracer.runfunc(traced_func_simple_caller, 1)
@@ -306,7 +305,7 @@ class TestFuncs(unittest.TestCase):
with self.assertRaises(TypeError):
self.tracer.runfunc()
# TODO: RUSTPYTHON, gc
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_loop_caller_importing(self):
self.tracer.runfunc(traced_func_importing_caller, 1)
@@ -320,10 +319,11 @@ class TestFuncs(unittest.TestCase):
}
self.assertEqual(self.tracer.results().calledfuncs, expected)
# TODO: RUSTPYTHON, gc
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'pre-existing trace function throws off measurements')
@requires_gil_enabled("gh-117783: immortalization of types affects traced method names")
def test_inst_method_calling(self):
obj = TracedClass(20)
self.tracer.runfunc(obj.inst_method_calling, 1)
@@ -335,7 +335,7 @@ class TestFuncs(unittest.TestCase):
}
self.assertEqual(self.tracer.results().calledfuncs, expected)
# TODO: RUSTPYTHON, gc
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_traced_decorated_function(self):
self.tracer.runfunc(traced_decorated_function)
@@ -357,9 +357,11 @@ class TestCallers(unittest.TestCase):
self.tracer = Trace(count=0, trace=0, countcallers=1)
self.filemod = my_file_and_modname()
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'pre-existing trace function throws off measurements')
@unittest.skip("TODO: RUSTPYTHON, Error in atexit._run_exitfuncs")
@requires_gil_enabled("gh-117783: immortalization of types affects traced method names")
def test_loop_caller_importing(self):
self.tracer.runfunc(traced_func_importing_caller, 1)
@@ -387,15 +389,21 @@ class TestCoverage(unittest.TestCase):
rmtree(TESTFN)
unlink(TESTFN)
def _coverage(self, tracer,
cmd='import test.support, test.test_pprint;'
'test.support.run_unittest(test.test_pprint.QueryTestCase)'):
DEFAULT_SCRIPT = '''if True:
import unittest
from test.test_pprint import QueryTestCase
loader = unittest.TestLoader()
tests = loader.loadTestsFromTestCase(QueryTestCase)
tests(unittest.TestResult())
'''
def _coverage(self, tracer, cmd=DEFAULT_SCRIPT):
tracer.run(cmd)
r = tracer.results()
r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@requires_resource('cpu')
def test_coverage(self):
tracer = trace.Trace(trace=0, count=1)
with captured_stdout() as stdout:
@@ -412,7 +420,7 @@ class TestCoverage(unittest.TestCase):
libpath = os.path.normpath(os.path.dirname(os.path.dirname(__file__)))
# sys.prefix does not work when running from a checkout
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
libpath], trace=0, count=1)
libpath] + sys.path, trace=0, count=1)
with captured_stdout() as stdout:
self._coverage(tracer)
if os.path.exists(TESTFN):
@@ -592,5 +600,31 @@ class TestCommandLine(unittest.TestCase):
assert_python_failure('-m', 'trace', '-l', '--module', 'not_a_module_zzz')
class TestTrace(unittest.TestCase):
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
self.tracer = Trace(count=0, trace=1)
self.filemod = my_file_and_modname()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_no_source_file(self):
filename = "<unknown>"
co = traced_func_linear.__code__
co = co.replace(co_filename=filename)
f = FunctionType(co, globals())
with captured_stdout() as out:
self.tracer.runfunc(f, 2, 3)
out = out.getvalue().splitlines()
firstlineno = get_firstlineno(f)
self.assertIn(f" --- modulename: {self.filemod[1]}, funcname: {f.__code__.co_name}", out[0])
self.assertIn(f"{filename}({firstlineno + 1})", out[1])
self.assertIn(f"{filename}({firstlineno + 2})", out[2])
self.assertIn(f"{filename}({firstlineno + 3})", out[3])
self.assertIn(f"{filename}({firstlineno + 4})", out[4])
if __name__ == '__main__':
unittest.main()

32
Lib/trace.py vendored
View File

@@ -49,6 +49,7 @@ Sample use, programmatically
"""
__all__ = ['Trace', 'CoverageResults']
import io
import linecache
import os
import sys
@@ -201,7 +202,8 @@ class CoverageResults:
for key in other_callers:
callers[key] = 1
def write_results(self, show_missing=True, summary=False, coverdir=None):
def write_results(self, show_missing=True, summary=False, coverdir=None, *,
ignore_missing_files=False):
"""
Write the coverage results.
@@ -210,6 +212,9 @@ class CoverageResults:
:param coverdir: If None, the results of each module are placed in its
directory, otherwise it is included in the directory
specified.
:param ignore_missing_files: If True, counts for files that no longer
exist are silently ignored. Otherwise, a missing file
will raise a FileNotFoundError.
"""
if self.calledfuncs:
print()
@@ -252,13 +257,15 @@ class CoverageResults:
if filename.endswith(".pyc"):
filename = filename[:-1]
if ignore_missing_files and not os.path.isfile(filename):
continue
if coverdir is None:
dir = os.path.dirname(os.path.abspath(filename))
modulename = _modname(filename)
else:
dir = coverdir
if not os.path.exists(dir):
os.makedirs(dir)
os.makedirs(dir, exist_ok=True)
modulename = _fullmodname(filename)
# If desired, get a list of the line numbers which represent
@@ -277,7 +284,6 @@ class CoverageResults:
percent = int(100 * n_hits / n_lines)
sums[modulename] = n_lines, percent, modulename, filename
if summary and sums:
print("lines cov% module (path)")
for m in sorted(sums):
@@ -559,8 +565,12 @@ class Trace:
if self.start_time:
print('%.2f' % (_time() - self.start_time), end=' ')
bname = os.path.basename(filename)
print("%s(%d): %s" % (bname, lineno,
linecache.getline(filename, lineno)), end='')
line = linecache.getline(filename, lineno)
print("%s(%d)" % (bname, lineno), end='')
if line:
print(": ", line, end='')
else:
print()
return self.localtrace
def localtrace_trace(self, frame, why, arg):
@@ -572,8 +582,12 @@ class Trace:
if self.start_time:
print('%.2f' % (_time() - self.start_time), end=' ')
bname = os.path.basename(filename)
print("%s(%d): %s" % (bname, lineno,
linecache.getline(filename, lineno)), end='')
line = linecache.getline(filename, lineno)
print("%s(%d)" % (bname, lineno), end='')
if line:
print(": ", line, end='')
else:
print()
return self.localtrace
def localtrace_count(self, frame, why, arg):
@@ -716,7 +730,7 @@ def main():
sys.argv = [opts.progname, *opts.arguments]
sys.path[0] = os.path.dirname(opts.progname)
with open(opts.progname, 'rb') as fp:
with io.open_code(opts.progname) as fp:
code = compile(fp.read(), opts.progname, 'exec')
# try to emulate __main__ namespace as much as possible
globs = {