summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst2
-rw-r--r--README.rst2
-rw-r--r--coverage/debug.py1
-rw-r--r--coverage/env.py3
-rw-r--r--coverage/execfile.py7
-rw-r--r--coverage/files.py7
-rw-r--r--doc/index.rst2
-rw-r--r--setup.py1
-rw-r--r--tests/test_execfile.py14
-rw-r--r--tests/test_process.py19
10 files changed, 45 insertions, 13 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index af1e5788..4eec16e9 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -32,6 +32,8 @@ Unreleased
registers `numbits_to_nums` for use in SQLite queries. Thanks, Simon
Willison.
+- Python 3.9a1 is supported.
+
.. _issue 745: https://github.com/nedbat/coveragepy/issues/745
.. _issue 838: https://github.com/nedbat/coveragepy/issues/838
diff --git a/README.rst b/README.rst
index 2fdd0620..96b60321 100644
--- a/README.rst
+++ b/README.rst
@@ -20,7 +20,7 @@ library to determine which lines are executable, and which have been executed.
Coverage.py runs on many versions of Python:
* CPython 2.7.
-* CPython 3.5 through 3.8.
+* CPython 3.5 through 3.9 alpha 1.
* PyPy2 7.0 and PyPy3 7.0.
Documentation is on `Read the Docs`_. Code repository and issue tracker are on
diff --git a/coverage/debug.py b/coverage/debug.py
index c713978a..c516ac5b 100644
--- a/coverage/debug.py
+++ b/coverage/debug.py
@@ -215,6 +215,7 @@ def simplify(v): # pragma: debugging
def pp(v): # pragma: debugging
"""Debug helper to pretty-print data, including SimpleNamespace objects."""
+ # Might not be needed in 3.9+
pprint.pprint(simplify(v))
diff --git a/coverage/env.py b/coverage/env.py
index bb335ab3..0d4642ef 100644
--- a/coverage/env.py
+++ b/coverage/env.py
@@ -78,6 +78,9 @@ class PYBEHAVIOR(object):
# Are while-true loops optimized into absolute jumps with no loop setup?
nix_while_true = (PYVERSION >= (3, 8))
+ # Python 3.9a1 made sys.argv[0] and other reported files absolute paths.
+ report_absolute_files = (PYVERSION >= (3, 9))
+
# Coverage.py specifics.
# Are we using the C-implemented trace function?
diff --git a/coverage/execfile.py b/coverage/execfile.py
index 5527d10a..7ac36dcf 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.py
@@ -13,6 +13,7 @@ import types
from coverage import env
from coverage.backward import BUILTINS
from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
+from coverage.files import python_reported_file
from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
from coverage.phystokens import compile_unicode
from coverage.python import get_python_source
@@ -158,6 +159,7 @@ class PyRunner(object):
except ImportError:
pass
else:
+ try_filename = python_reported_file(try_filename)
self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
self.spec.has_location = True
self.package = ""
@@ -167,6 +169,9 @@ class PyRunner(object):
if env.PY3:
self.loader = DummyLoader("__main__")
+ self.args[0] = python_reported_file(self.args[0])
+ self.arg0 = python_reported_file(self.arg0)
+
if self.modulename is None:
self.modulename = '__main__'
@@ -179,7 +184,7 @@ class PyRunner(object):
top_file = inspect.stack()[-1][0].f_code.co_filename
if os.path.abspath(sys.path[0]) == os.path.abspath(os.path.dirname(top_file)):
# Set sys.path correctly.
- sys.path[0] = path0
+ sys.path[0] = python_reported_file(path0)
def run(self):
"""Run the Python code!"""
diff --git a/coverage/files.py b/coverage/files.py
index dc8c248f..2836d4e5 100644
--- a/coverage/files.py
+++ b/coverage/files.py
@@ -172,6 +172,13 @@ def abs_file(filename):
return path
+def python_reported_file(filename):
+ """Return the string as Python would describe this file name."""
+ if env.PYBEHAVIOR.report_absolute_files:
+ filename = os.path.abspath(filename)
+ return filename
+
+
RELATIVE_DIR = None
CANONICAL_FILENAME_CACHE = None
set_relative_directory()
diff --git a/doc/index.rst b/doc/index.rst
index 3e40ce2c..5e32e08a 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -31,7 +31,7 @@ not.
The latest version is coverage.py 5.0b1, released November 11, 2019.
It is supported on:
- * Python versions 2.7, 3.5, 3.6, 3.7, and 3.8.
+ * Python versions 2.7, 3.5, 3.6, 3.7, 3.8, and 3.9 alpha.
* PyPy2 7.2.0 and PyPy3 7.2.0.
diff --git a/setup.py b/setup.py
index 10973740..8c837d72 100644
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,7 @@ Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
+Programming Language :: Python :: 3.9
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Software Development :: Quality Assurance
diff --git a/tests/test_execfile.py b/tests/test_execfile.py
index 7ced605a..e3d62005 100644
--- a/tests/test_execfile.py
+++ b/tests/test_execfile.py
@@ -14,6 +14,7 @@ import sys
from coverage import env
from coverage.backward import binary_bytes
from coverage.execfile import run_python_file, run_python_module
+from coverage.files import python_reported_file
from coverage.misc import NoCode, NoSource
from tests.coveragetest import CoverageTest, TESTS_DIR, UsingModulesMixin
@@ -45,7 +46,7 @@ class RunFileTest(CoverageTest):
self.assertEqual(mod_globs['__main__.DATA'], "xyzzy")
# Argv should have the proper values.
- self.assertEqual(mod_globs['argv0'], TRY_EXECFILE)
+ self.assertEqual(mod_globs['argv0'], python_reported_file(TRY_EXECFILE))
self.assertEqual(mod_globs['argv1-n'], ["arg1", "arg2"])
# __builtins__ should have the right values, like open().
@@ -84,7 +85,9 @@ class RunFileTest(CoverageTest):
self.assertEqual(self.stdout(), "a is 1\n")
def test_no_such_file(self):
- with self.assertRaisesRegex(NoSource, "No file to run: 'xyzzy.py'"):
+ path = python_reported_file('xyzzy.py')
+ msg = re.escape("No file to run: '{}'".format(path))
+ with self.assertRaisesRegex(NoSource, msg):
run_python_file(["xyzzy.py"])
def test_directory_with_main(self):
@@ -156,7 +159,9 @@ class RunPycFileTest(CoverageTest):
os.remove(pycfile)
def test_no_such_pyc_file(self):
- with self.assertRaisesRegex(NoCode, "No file to run: 'xyzzy.pyc'"):
+ path = python_reported_file('xyzzy.pyc')
+ msg = re.escape("No file to run: '{}'".format(path))
+ with self.assertRaisesRegex(NoCode, msg):
run_python_file(["xyzzy.pyc"])
def test_running_py_from_binary(self):
@@ -166,8 +171,9 @@ class RunPycFileTest(CoverageTest):
with open(bf, "wb") as f:
f.write(b'\x7fELF\x02\x01\x01\x00\x00\x00')
+ path = python_reported_file('binary')
msg = (
- r"Couldn't run 'binary' as Python code: "
+ re.escape("Couldn't run '{}' as Python code: ".format(path)) +
r"(TypeError|ValueError): "
r"("
r"compile\(\) expected string without null bytes" # for py2
diff --git a/tests/test_process.py b/tests/test_process.py
index 06e429dd..e9e19e8a 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -18,6 +18,7 @@ import pytest
import coverage
from coverage import env
from coverage.data import line_counts
+from coverage.files import python_reported_file
from coverage.misc import output_encoding
from tests.coveragetest import CoverageTest
@@ -472,9 +473,10 @@ class ProcessTest(CoverageTest):
self.assertMultiLineEqual(out, out2)
# But also make sure that the output is what we expect.
- self.assertIn('File "throw.py", line 5, in f2', out)
+ path = python_reported_file('throw.py')
+ msg = 'File "{}", line 5,? in f2'.format(re.escape(path))
+ self.assertRegex(out, msg)
self.assertIn('raise Exception("hey!")', out)
- self.assertNotIn('coverage', out)
self.assertEqual(status, 1)
def test_code_exits(self):
@@ -582,9 +584,13 @@ class ProcessTest(CoverageTest):
out = self.run_command("coverage html")
self.assertEqual(out.count("Module xyzzy was never imported."), 0)
- def test_warnings_if_never_run(self):
+ def test_warns_if_never_run(self):
+ # Note: the name of the function can't have "warning" in it, or the
+ # absolute path of the file will have "warning" in it, and an assertion
+ # will fail.
out = self.run_command("coverage run i_dont_exist.py")
- self.assertIn("No file to run: 'i_dont_exist.py'", out)
+ path = python_reported_file('i_dont_exist.py')
+ self.assertIn("No file to run: '{}'".format(path), out)
self.assertNotIn("warning", out)
self.assertNotIn("Exception", out)
@@ -1321,12 +1327,13 @@ class YankedDirectoryTest(CoverageTest):
def test_removing_directory_with_error(self):
self.make_file("bug806.py", self.BUG_806)
out = self.run_command("coverage run bug806.py")
+ path = python_reported_file('bug806.py')
self.assertEqual(out, textwrap.dedent("""\
Traceback (most recent call last):
- File "bug806.py", line 8, in <module>
+ File "{}", line 8, in <module>
print(sys.argv[1])
IndexError: list index out of range
- """))
+ """.format(path)))
def possible_pth_dirs():