Skip to content

Commit 6a9579e

Browse files
authored
Update trace.py from 3.13.5 (RustPython#6029)
1 parent 8621b3d commit 6a9579e

File tree

2 files changed

+71
-23
lines changed

2 files changed

+71
-23
lines changed

Lib/test/test_trace.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import os
22
from pickle import dump
33
import sys
4-
from test.support import captured_stdout
4+
from test.support import captured_stdout, requires_resource, requires_gil_enabled
55
from test.support.os_helper import (TESTFN, rmtree, unlink)
66
from test.support.script_helper import assert_python_ok, assert_python_failure
77
import textwrap
88
import unittest
9+
from types import FunctionType
910

1011
import trace
1112
from trace import Trace
@@ -197,9 +198,7 @@ def test_trace_list_comprehension(self):
197198
firstlineno_called = get_firstlineno(traced_doubler)
198199
expected = {
199200
(self.my_py_filename, firstlineno_calling + 1): 1,
200-
# List comprehensions work differently in 3.x, so the count
201-
# below changed compared to 2.x.
202-
(self.my_py_filename, firstlineno_calling + 2): 12,
201+
(self.my_py_filename, firstlineno_calling + 2): 11,
203202
(self.my_py_filename, firstlineno_calling + 3): 1,
204203
(self.my_py_filename, firstlineno_called + 1): 10,
205204
}
@@ -251,7 +250,7 @@ def setUp(self):
251250
self.my_py_filename = fix_ext_py(__file__)
252251
self.addCleanup(sys.settrace, sys.gettrace())
253252

254-
# TODO: RUSTPYTHON, KeyError: ('Lib/test/test_trace.py', 43)
253+
# TODO: RUSTPYTHON
255254
@unittest.expectedFailure
256255
def test_exec_counts(self):
257256
self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
@@ -287,7 +286,7 @@ def tearDown(self):
287286
if self._saved_tracefunc is not None:
288287
sys.settrace(self._saved_tracefunc)
289288

290-
# TODO: RUSTPYTHON, gc
289+
# TODO: RUSTPYTHON
291290
@unittest.expectedFailure
292291
def test_simple_caller(self):
293292
self.tracer.runfunc(traced_func_simple_caller, 1)
@@ -306,7 +305,7 @@ def test_arg_errors(self):
306305
with self.assertRaises(TypeError):
307306
self.tracer.runfunc()
308307

309-
# TODO: RUSTPYTHON, gc
308+
# TODO: RUSTPYTHON
310309
@unittest.expectedFailure
311310
def test_loop_caller_importing(self):
312311
self.tracer.runfunc(traced_func_importing_caller, 1)
@@ -320,10 +319,11 @@ def test_loop_caller_importing(self):
320319
}
321320
self.assertEqual(self.tracer.results().calledfuncs, expected)
322321

323-
# TODO: RUSTPYTHON, gc
322+
# TODO: RUSTPYTHON
324323
@unittest.expectedFailure
325324
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
326325
'pre-existing trace function throws off measurements')
326+
@requires_gil_enabled("gh-117783: immortalization of types affects traced method names")
327327
def test_inst_method_calling(self):
328328
obj = TracedClass(20)
329329
self.tracer.runfunc(obj.inst_method_calling, 1)
@@ -335,7 +335,7 @@ def test_inst_method_calling(self):
335335
}
336336
self.assertEqual(self.tracer.results().calledfuncs, expected)
337337

338-
# TODO: RUSTPYTHON, gc
338+
# TODO: RUSTPYTHON
339339
@unittest.expectedFailure
340340
def test_traced_decorated_function(self):
341341
self.tracer.runfunc(traced_decorated_function)
@@ -357,9 +357,11 @@ def setUp(self):
357357
self.tracer = Trace(count=0, trace=0, countcallers=1)
358358
self.filemod = my_file_and_modname()
359359

360+
# TODO: RUSTPYTHON
361+
@unittest.expectedFailure
360362
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
361363
'pre-existing trace function throws off measurements')
362-
@unittest.skip("TODO: RUSTPYTHON, Error in atexit._run_exitfuncs")
364+
@requires_gil_enabled("gh-117783: immortalization of types affects traced method names")
363365
def test_loop_caller_importing(self):
364366
self.tracer.runfunc(traced_func_importing_caller, 1)
365367

@@ -387,15 +389,21 @@ def tearDown(self):
387389
rmtree(TESTFN)
388390
unlink(TESTFN)
389391

390-
def _coverage(self, tracer,
391-
cmd='import test.support, test.test_pprint;'
392-
'test.support.run_unittest(test.test_pprint.QueryTestCase)'):
392+
DEFAULT_SCRIPT = '''if True:
393+
import unittest
394+
from test.test_pprint import QueryTestCase
395+
loader = unittest.TestLoader()
396+
tests = loader.loadTestsFromTestCase(QueryTestCase)
397+
tests(unittest.TestResult())
398+
'''
399+
def _coverage(self, tracer, cmd=DEFAULT_SCRIPT):
393400
tracer.run(cmd)
394401
r = tracer.results()
395402
r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
396403

397404
# TODO: RUSTPYTHON
398405
@unittest.expectedFailure
406+
@requires_resource('cpu')
399407
def test_coverage(self):
400408
tracer = trace.Trace(trace=0, count=1)
401409
with captured_stdout() as stdout:
@@ -412,7 +420,7 @@ def test_coverage_ignore(self):
412420
libpath = os.path.normpath(os.path.dirname(os.path.dirname(__file__)))
413421
# sys.prefix does not work when running from a checkout
414422
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
415-
libpath], trace=0, count=1)
423+
libpath] + sys.path, trace=0, count=1)
416424
with captured_stdout() as stdout:
417425
self._coverage(tracer)
418426
if os.path.exists(TESTFN):
@@ -592,5 +600,31 @@ def test_run_as_module(self):
592600
assert_python_failure('-m', 'trace', '-l', '--module', 'not_a_module_zzz')
593601

594602

603+
class TestTrace(unittest.TestCase):
604+
def setUp(self):
605+
self.addCleanup(sys.settrace, sys.gettrace())
606+
self.tracer = Trace(count=0, trace=1)
607+
self.filemod = my_file_and_modname()
608+
609+
# TODO: RUSTPYTHON
610+
@unittest.expectedFailure
611+
def test_no_source_file(self):
612+
filename = "<unknown>"
613+
co = traced_func_linear.__code__
614+
co = co.replace(co_filename=filename)
615+
f = FunctionType(co, globals())
616+
617+
with captured_stdout() as out:
618+
self.tracer.runfunc(f, 2, 3)
619+
620+
out = out.getvalue().splitlines()
621+
firstlineno = get_firstlineno(f)
622+
self.assertIn(f" --- modulename: {self.filemod[1]}, funcname: {f.__code__.co_name}", out[0])
623+
self.assertIn(f"{filename}({firstlineno + 1})", out[1])
624+
self.assertIn(f"{filename}({firstlineno + 2})", out[2])
625+
self.assertIn(f"{filename}({firstlineno + 3})", out[3])
626+
self.assertIn(f"{filename}({firstlineno + 4})", out[4])
627+
628+
595629
if __name__ == '__main__':
596630
unittest.main()

Lib/trace.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"""
5050
__all__ = ['Trace', 'CoverageResults']
5151

52+
import io
5253
import linecache
5354
import os
5455
import sys
@@ -201,7 +202,8 @@ def update(self, other):
201202
for key in other_callers:
202203
callers[key] = 1
203204

204-
def write_results(self, show_missing=True, summary=False, coverdir=None):
205+
def write_results(self, show_missing=True, summary=False, coverdir=None, *,
206+
ignore_missing_files=False):
205207
"""
206208
Write the coverage results.
207209
@@ -210,6 +212,9 @@ def write_results(self, show_missing=True, summary=False, coverdir=None):
210212
:param coverdir: If None, the results of each module are placed in its
211213
directory, otherwise it is included in the directory
212214
specified.
215+
:param ignore_missing_files: If True, counts for files that no longer
216+
exist are silently ignored. Otherwise, a missing file
217+
will raise a FileNotFoundError.
213218
"""
214219
if self.calledfuncs:
215220
print()
@@ -252,13 +257,15 @@ def write_results(self, show_missing=True, summary=False, coverdir=None):
252257
if filename.endswith(".pyc"):
253258
filename = filename[:-1]
254259

260+
if ignore_missing_files and not os.path.isfile(filename):
261+
continue
262+
255263
if coverdir is None:
256264
dir = os.path.dirname(os.path.abspath(filename))
257265
modulename = _modname(filename)
258266
else:
259267
dir = coverdir
260-
if not os.path.exists(dir):
261-
os.makedirs(dir)
268+
os.makedirs(dir, exist_ok=True)
262269
modulename = _fullmodname(filename)
263270

264271
# If desired, get a list of the line numbers which represent
@@ -277,7 +284,6 @@ def write_results(self, show_missing=True, summary=False, coverdir=None):
277284
percent = int(100 * n_hits / n_lines)
278285
sums[modulename] = n_lines, percent, modulename, filename
279286

280-
281287
if summary and sums:
282288
print("lines cov% module (path)")
283289
for m in sorted(sums):
@@ -559,8 +565,12 @@ def localtrace_trace_and_count(self, frame, why, arg):
559565
if self.start_time:
560566
print('%.2f' % (_time() - self.start_time), end=' ')
561567
bname = os.path.basename(filename)
562-
print("%s(%d): %s" % (bname, lineno,
563-
linecache.getline(filename, lineno)), end='')
568+
line = linecache.getline(filename, lineno)
569+
print("%s(%d)" % (bname, lineno), end='')
570+
if line:
571+
print(": ", line, end='')
572+
else:
573+
print()
564574
return self.localtrace
565575

566576
def localtrace_trace(self, frame, why, arg):
@@ -572,8 +582,12 @@ def localtrace_trace(self, frame, why, arg):
572582
if self.start_time:
573583
print('%.2f' % (_time() - self.start_time), end=' ')
574584
bname = os.path.basename(filename)
575-
print("%s(%d): %s" % (bname, lineno,
576-
linecache.getline(filename, lineno)), end='')
585+
line = linecache.getline(filename, lineno)
586+
print("%s(%d)" % (bname, lineno), end='')
587+
if line:
588+
print(": ", line, end='')
589+
else:
590+
print()
577591
return self.localtrace
578592

579593
def localtrace_count(self, frame, why, arg):
@@ -716,7 +730,7 @@ def parse_ignore_dir(s):
716730
sys.argv = [opts.progname, *opts.arguments]
717731
sys.path[0] = os.path.dirname(opts.progname)
718732

719-
with open(opts.progname, 'rb') as fp:
733+
with io.open_code(opts.progname) as fp:
720734
code = compile(fp.read(), opts.progname, 'exec')
721735
# try to emulate __main__ namespace as much as possible
722736
globs = {

0 commit comments

Comments
 (0)