Skip to content

runpy.run_path mishandles Path objects #99437

Closed
@nedbat

Description

@nedbat

Bug report

runpy.run_path will accept a Path object, and run it, but various code inspection tools will be unhappy with it because it is not a string. Should run_path convert its argument to a true string before execution?

Coverage.py is one of those tools; this report is based on nedbat/coveragepy#1417.

Create debugme.py:

from pathlib import Path
import runpy, sys

runme = Path(__file__).resolve().parent / sys.argv[1]
print(f"Running {runme!r}")
runpy.run_path(runme)

Create inspectme.py:

import inspect

print(inspect.stack())
print('here')

Run it:

% python3.12 debugme.py inspectme.py
Running PosixPath('/System/Volumes/Data/root/src/bugs/bug1417/inspectme.py')
Traceback (most recent call last):
  File "/System/Volumes/Data/root/src/bugs/bug1417/debugme.py", line 6, in <module>
    runpy.run_path(runme)
  File "<frozen runpy>", line 291, in run_path
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/System/Volumes/Data/root/src/bugs/bug1417/inspectme.py", line 3, in <module>
    print(inspect.stack())
          ^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 1748, in stack
    return getouterframes(sys._getframe(1), context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 1723, in getouterframes
    traceback_info = getframeinfo(frame, context)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 1685, in getframeinfo
    lines, lnum = findsource(frame)
                  ^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 1060, in findsource
    module = getmodule(object, file)
             ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 983, in getmodule
    f = getabsfile(module)
        ^^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 952, in getabsfile
    _filename = getsourcefile(object) or getfile(object)
                ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 928, in getsourcefile
    if any(filename.endswith(s) for s in all_bytecode_suffixes):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/pyenv/pyenv/versions/3.12.0a1/lib/python3.12/inspect.py", line 928, in <genexpr>
    if any(filename.endswith(s) for s in all_bytecode_suffixes):
           ^^^^^^^^^^^^^^^^^
AttributeError: 'PosixPath' object has no attribute 'endswith'

Coverage.py fails earlier, but for a similar reason:

% coverage run debugme.py inspectme.py
Running PosixPath('/System/Volumes/Data/root/src/bugs/bug1417/inspectme.py')
Traceback (most recent call last):
  File "/System/Volumes/Data/root/src/bugs/bug1417/debugme.py", line 6, in <module>
    runpy.run_path(runme)
  File "/usr/local/pyenv/pyenv/versions/3.9.15/lib/python3.9/runpy.py", line 288, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/usr/local/pyenv/pyenv/versions/3.9.15/lib/python3.9/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/local/pyenv/pyenv/versions/3.9.15/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/control.py", line 344, in _should_trace
    disp = self._inorout.should_trace(filename, frame)
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/inorout.py", line 335, in should_trace
    filename = source_for_file(dunder_file)
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/python.py", line 105, in source_for_file
    if filename.endswith(".py"):
AttributeError: 'PosixPath' object has no attribute 'endswith'

Using coverage.py's --timid flag uses a Python trace function and also shows the error:

% coverage run --timid debugme.py inspectme.py
Running PosixPath('/System/Volumes/Data/root/src/bugs/bug1417/inspectme.py')
Traceback (most recent call last):
  File "/System/Volumes/Data/root/src/bugs/bug1417/debugme.py", line 6, in <module>
    runpy.run_path(runme)
  File "/usr/local/pyenv/pyenv/versions/3.9.15/lib/python3.9/runpy.py", line 288, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/usr/local/pyenv/pyenv/versions/3.9.15/lib/python3.9/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/local/pyenv/pyenv/versions/3.9.15/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/System/Volumes/Data/root/src/bugs/bug1417/inspectme.py", line 1, in <module>
    import inspect
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/pytracer.py", line 166, in _trace
    disp = self.should_trace(filename, frame)
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/control.py", line 344, in _should_trace
    disp = self._inorout.should_trace(filename, frame)
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/inorout.py", line 335, in should_trace
    filename = source_for_file(dunder_file)
  File "/usr/local/virtualenvs/tmp-6c4153bbeaa11b2/lib/python3.9/site-packages/coverage/python.py", line 105, in source_for_file
    if filename.endswith(".py"):
AttributeError: 'PosixPath' object has no attribute 'endswith'

I don't understand why a simpler trace example gets a string for co_filename? Create traceme.py:

import sys

def foo():
    print("FOO")

def trace(frame, event, arg):
    if "traceme" in str(frame.f_code.co_filename):
        print(type(frame.f_code.co_filename))
        print(f"{frame.f_code.co_filename=}")

sys.settrace(trace)

foo()

print('here')

and run it:

% python debugme.py traceme.py
Running PosixPath('/System/Volumes/Data/root/src/bugs/bug1417/traceme.py')
<class 'str'>
frame.f_code.co_filename='/System/Volumes/Data/root/src/bugs/bug1417/traceme.py'
FOO
here

Your environment

I'm on a Mac, and this behavior seems consistent on versions 3.7 through 3.12.0a1.

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibPython modules in the Lib dirtopic-importlibtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions