diff --git a/CHANGES.rst b/CHANGES.rst index 919379529..765695f85 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,7 +20,11 @@ development at the same time, such as 4.5.x and 5.0. Unreleased ---------- -Nothing yet. +- Fix: On Python 3.7, a file with type annotations but no ``from __future__ + import annotations`` would be missing statements in the coverage report. This + is now fixed, closing `issue 1524`_. + +.. _issue 1524: https://github.com/nedbat/coveragepy/issues/1524 .. _changes_7-0-4: diff --git a/coverage/execfile.py b/coverage/execfile.py index f0f4f171d..d26da65bb 100644 --- a/coverage/execfile.py +++ b/coverage/execfile.py @@ -275,7 +275,7 @@ def make_code_from_py(filename): except (OSError, NoSource) as exc: raise NoSource(f"No file to run: '{filename}'") from exc - return compile(source, filename, "exec") + return compile(source, filename, "exec", dont_inherit=True) def make_code_from_pyc(filename): diff --git a/coverage/parser.py b/coverage/parser.py index 37d747674..b8ddb5015 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -385,7 +385,7 @@ def __init__( else: assert filename is not None try: - self.code = compile(text, filename, "exec") + self.code = compile(text, filename, "exec", dont_inherit=True) except SyntaxError as synerr: raise NotPython( "Couldn't parse '%s' as Python source: '%s' at line %d" % ( diff --git a/lab/genpy.py b/lab/genpy.py index f968c9163..f88e70ca8 100644 --- a/lab/genpy.py +++ b/lab/genpy.py @@ -231,7 +231,7 @@ def show_a_bunch(): source = PythonSpinner.generate_python(maker.make_body("def")) try: print("-"*80, "\n", source, sep="") - compile(source, "", "exec") + compile(source, "", "exec", dont_inherit=True) except Exception as ex: print(f"Oops: {ex}\n{source}") if len(source) > len(longest): diff --git a/lab/parser.py b/lab/parser.py index ebd4e7f3a..c7687bda6 100644 --- a/lab/parser.py +++ b/lab/parser.py @@ -177,7 +177,7 @@ def all_code_objects(code): def disassemble(pyparser): """Disassemble code, for ad-hoc experimenting.""" - code = compile(pyparser.text, "", "exec") + code = compile(pyparser.text, "", "exec", dont_inherit=True) for code_obj in all_code_objects(code): if pyparser.text: srclines = pyparser.text.splitlines() diff --git a/lab/show_pyc.py b/lab/show_pyc.py index e346930a5..1bd98ec64 100644 --- a/lab/show_pyc.py +++ b/lab/show_pyc.py @@ -48,7 +48,7 @@ def show_py_file(fname): show_py_text(text, fname=fname) def show_py_text(text, fname=""): - code = compile(text, fname, "exec") + code = compile(text, fname, "exec", dont_inherit=True) show_code(code) CO_FLAGS = [ diff --git a/setup.py b/setup.py index c30907f92..dd7676013 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ def better_set_verbosity(v): # Keep pylint happy. __version__ = __url__ = version_info = "" # Execute the code in version.py. - exec(compile(version_file.read(), cov_ver_py, 'exec')) + exec(compile(version_file.read(), cov_ver_py, 'exec', dont_inherit=True)) with open("README.rst") as readme: readme_text = readme.read() diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 6caac307f..c517d39d3 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -142,7 +142,7 @@ def cmd_executes( code = textwrap.dedent(code) expected = self.model_object() globs = {n: getattr(expected, n) for n in self.MOCK_GLOBALS} - code_obj = compile(code, "", "exec") + code_obj = compile(code, "", "exec", dont_inherit=True) eval(code_obj, globs, {}) # pylint: disable=eval-used # Many of our functions take a lot of arguments, and cmdline.py diff --git a/tests/test_summary.py b/tests/test_summary.py index 3109e90f2..f532a7b1f 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -849,6 +849,22 @@ def missing(x, y): assert self.get_report(cov, output_format="total", precision=2) == "78.57\n" assert self.get_report(cov, output_format="total", precision=4) == "78.5714\n" + def test_bug_1524(self) -> None: + self.make_file("bug1524.py", """\ + class Mine: + @property + def thing(self) -> int: + return 17 + + print(Mine().thing) + """) + cov = coverage.Coverage() + self.start_import_stop(cov, "bug1524") + assert self.stdout() == "17\n" + report = self.get_report(cov) + report_lines = report.splitlines() + assert report_lines[2] == "bug1524.py 5 0 100%" + class ReportingReturnValueTest(CoverageTest): """Tests of reporting functions returning values."""