X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 48 additions & 14 deletions Lib/test/test_trace.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -197,9 +198,7 @@ def test_trace_list_comprehension(self):
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,
}
Expand Down Expand Up @@ -251,7 +250,7 @@ def setUp(self):
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)
Expand Down Expand Up @@ -287,7 +286,7 @@ def tearDown(self):
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)
Expand All @@ -306,7 +305,7 @@ def test_arg_errors(self):
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)
Expand All @@ -320,10 +319,11 @@ def test_loop_caller_importing(self):
}
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)
Expand All @@ -335,7 +335,7 @@ def test_inst_method_calling(self):
}
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)
Expand All @@ -357,9 +357,11 @@ def setUp(self):
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)

Expand Down Expand Up @@ -387,15 +389,21 @@ def tearDown(self):
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:
Expand All @@ -412,7 +420,7 @@ def test_coverage_ignore(self):
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):
Expand Down Expand Up @@ -592,5 +600,31 @@ def test_run_as_module(self):
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 changes: 23 additions & 9 deletions Lib/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"""
__all__ = ['Trace', 'CoverageResults']

import io
import linecache
import os
import sys
Expand Down Expand Up @@ -201,7 +202,8 @@ def update(self, other):
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.

Expand All @@ -210,6 +212,9 @@ def write_results(self, show_missing=True, summary=False, coverdir=None):
: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()
Expand Down Expand Up @@ -252,13 +257,15 @@ def write_results(self, show_missing=True, summary=False, coverdir=None):
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
Expand All @@ -277,7 +284,6 @@ def write_results(self, show_missing=True, summary=False, coverdir=None):
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):
Expand Down Expand Up @@ -559,8 +565,12 @@ def localtrace_trace_and_count(self, frame, why, arg):
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):
Expand All @@ -572,8 +582,12 @@ def localtrace_trace(self, frame, why, arg):
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):
Expand Down Expand Up @@ -716,7 +730,7 @@ def parse_ignore_dir(s):
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 = {
Expand Down
Loading
X Tutup