diff --git a/CHANGES.rst b/CHANGES.rst index 49f5dc9a8..966a56b3d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -30,6 +30,12 @@ Unreleased - Feature: The HTML report pages for Python source files now have a sticky header so the file name and controls are always visible. +- Fix: more generated code is now excluded from measurement. Code such as + `attrs`_ boilerplate, or doctest code, was being measured though the + synthetic line numbers meant they were never reported. Once Cython was + involved though, the generated .so files were parsed as Python, raising + syntax errors, as reported in `issue 1160`_. This is now fixed. + - Fix: When sorting human-readable names, numeric components are sorted correctly: file10.py will appear after file9.py. This applies to file names, module names, environment variables, and test contexts. @@ -41,6 +47,8 @@ Unreleased .. _issue 553: https://github.com/nedbat/coveragepy/issues/553 .. _issue 840: https://github.com/nedbat/coveragepy/issues/840 +.. _issue 1160: https://github.com/nedbat/coveragepy/issues/1160 +.. _attrs: https://www.attrs.org/ .. _changes_602: diff --git a/coverage/disposition.py b/coverage/disposition.py index dfcc6def3..1c39a9c19 100644 --- a/coverage/disposition.py +++ b/coverage/disposition.py @@ -30,6 +30,8 @@ def disposition_debug_msg(disp): """Make a nice debug message of what the FileDisposition is doing.""" if disp.trace: msg = f"Tracing {disp.original_filename!r}" + if disp.original_filename != disp.source_filename: + msg += f" as {disp.source_filename!r}" if disp.file_tracer: msg += ": will be traced by %r" % disp.file_tracer else: diff --git a/coverage/inorout.py b/coverage/inorout.py index 6bdac06e5..3bc7e54e4 100644 --- a/coverage/inorout.py +++ b/coverage/inorout.py @@ -317,6 +317,9 @@ def nope(disp, reason): disp.reason = reason return disp + if original_filename.startswith('<'): + return nope(disp, "not a real original file name") + if frame is not None: # Compiled Python files have two file names: frame.f_code.co_filename is # the file name at the time the .pyc was compiled. The second name is diff --git a/tests/test_oddball.py b/tests/test_oddball.py index c3082abbc..b59e6395e 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -393,7 +393,13 @@ class DoctestTest(CoverageTest): """Tests invoked with doctest should measure properly.""" def test_doctest(self): - self.check_coverage('''\ + # Doctests used to be traced, with their line numbers credited to the + # file they were in. Below, one of the doctests has four lines (1-4), + # which would incorrectly claim that lines 1-4 of the file were + # executed. In this file, line 2 is not executed. + self.make_file("the_doctest.py", '''\ + if "x" in "abc": + print("hello") def return_arg_or_void(arg): """If is None, return "Void"; otherwise return @@ -403,6 +409,11 @@ def return_arg_or_void(arg): 'arg' >>> return_arg_or_void("None") 'None' + >>> if "x" in "xyz": # line 1 + ... if "a" in "aswed": # line 2 + ... if "a" in "abc": # line 3 + ... return_arg_or_void(12) # line 4 + 12 """ if arg is None: return "Void" @@ -411,8 +422,13 @@ def return_arg_or_void(arg): import doctest, sys doctest.testmod(sys.modules[__name__]) # we're not __main__ :( - ''', - [1, 11, 12, 14, 16, 17], "") + ''') + cov = coverage.Coverage() + self.start_import_stop(cov, "the_doctest") + data = cov.get_data() + assert len(data.measured_files()) == 1 + lines = data.lines(data.measured_files().pop()) + assert lines == [1, 3, 18, 19, 21, 23, 24] class GettraceTest(CoverageTest):