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))
+        """)