fix: runpy.run_path(path) crashes with pathlib.Path (#1819)
* fix: runpy.run_path(path) crashes with pathlib.Path
* Revert "fix: runpy.run_path(path) crashes with pathlib.Path"
This reverts commit 2d1eabda2710415aa02166529238e7ce6257678c.
* fix: runpy.run_path(path) crashes with pathlib.Path
* test: runpy.run_path(path) with being pathlib.Path
* finish up #1819
* test both str and Path
---------
Co-authored-by: Ned Batchelder <ned@nedbatchelder.com>
GitOrigin-RevId: 7fb846e8c12b33f9f82964bbb1cef7cc2795d42d
Change-Id: Ifddce5f971f3390fb2854f0932b7ccf8baa13d5b
diff --git a/CHANGES.rst b/CHANGES.rst
index ff40f69..139d4d5 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -23,7 +23,11 @@
Unreleased
----------
-Nothing yet.
+- Fix: coverage used to fail when measuring code using :func:`runpy.run_path
+ <python:runpy.run_path>` with a :class:`Path <python:pathlib.Path>` argument.
+ This is now fixed, thanks to `Ask Hjorth Larsen <pull 1819_>`_.
+
+.. _pull 1819: https://github.com/nedbat/coveragepy/pull/1819/files
.. scriv-start-here
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 728f2fb..e44920c 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -25,6 +25,7 @@
Arthur Deygin
Arthur Rio
Asher Foa
+Ask Hjorth Larsen
Ben Carlsson
Ben Finney
Benjamin Parzella
diff --git a/coverage/inorout.py b/coverage/inorout.py
index 5ea29ed..4df54b2 100644
--- a/coverage/inorout.py
+++ b/coverage/inorout.py
@@ -321,7 +321,8 @@
# co_filename value.
dunder_file = frame.f_globals and frame.f_globals.get("__file__")
if dunder_file:
- filename = source_for_file(dunder_file)
+ # Danger: __file__ can (rarely?) be of type Path.
+ filename = source_for_file(str(dunder_file))
if original_filename and not original_filename.startswith("<"):
orig = os.path.basename(original_filename)
if orig != os.path.basename(filename):
diff --git a/tests/test_python.py b/tests/test_python.py
index ee0268f..6a83629 100644
--- a/tests/test_python.py
+++ b/tests/test_python.py
@@ -63,3 +63,24 @@
# If both pyw and py exist, py is preferred
a_py.write_text("")
assert source_for_file(src + 'c') == src
+
+
+class RunpyTest(CoverageTest):
+ """Tests using runpy."""
+
+ @pytest.mark.parametrize("convert_to", ["str", "Path"])
+ def test_runpy_path(self, convert_to: str) -> None:
+ # Ensure runpy.run_path(path) works when path is pathlib.Path or str.
+ #
+ # runpy.run_path(pathlib.Path(...)) causes __file__ to be a Path,
+ # which may make source_for_file() stumble (#1819) with:
+ #
+ # AttributeError: 'PosixPath' object has no attribute 'endswith'
+
+ self.check_coverage(f"""\
+ import runpy
+ from pathlib import Path
+ pyfile = Path('script.py')
+ pyfile.write_text('')
+ runpy.run_path({convert_to}(pyfile))
+ """)