Bug report
Background
I have been writing a set of guided workshops on Tachyon as a way of learning it (https://github.com/GrahamDumpleton/tachyon-workshops), using an AI agent to generate the workshop content. One of the example programs being profiled in a workshop is a script that imports a helper module from its own directory. The agent found that the script ran fine as python dir/script.py, but failed with an import error as python -m profiling.sampling run dir/script.py. I am reporting it in case it is unintended; it may be a known difference.
What happens
When a script is run with the run subcommand from a directory other than the script's own, sys.path[0] is the current working directory, not the script's directory. A script that imports a module beside it works with python and fails with ModuleNotFoundError under the profiler.
$ mkdir -p w/sub && cd w
$ printf 'import sys\nprint("path0:", repr(sys.path[0]), "argv0:", sys.argv[0])\nimport helper\n' > sub/where.py
$ printf 'print("helper imported")\n' > sub/helper.py
$ python sub/where.py
path0: '/home/tester/w/sub' argv0: sub/where.py
helper imported
$ python -m profiling.sampling run sub/where.py
path0: '/home/tester/w' argv0: /home/tester/w/sub/where.py
Traceback (most recent call last):
File "<frozen runpy>", line 201, in _run_module_as_main
File "<frozen runpy>", line 87, in _run_code
File ".../lib/python3.15/profiling/sampling/_sync_coordinator.py", line 256, in <module>
main()
~~~~^^
File ".../lib/python3.15/profiling/sampling/_sync_coordinator.py", line 249, in main
_execute_script(script_path, script_args, cwd)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../lib/python3.15/profiling/sampling/_sync_coordinator.py", line 196, in _execute_script
exec(code, main_module.__dict__)
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tester/w/sub/where.py", line 3, in <module>
import helper
ModuleNotFoundError: No module named 'helper'
Captured 43 samples in 0.04 seconds
Running from inside the script's directory works, since the working directory and the script's directory are then the same:
$ cd sub && python -m profiling.sampling run where.py
path0: '/home/tester/w/sub' argv0: /home/tester/w/sub/where.py
helper imported
Tested on 3.15.0rc2 (python-build-standalone build installed by uv, and the python:3.15-rc-slim image) on Linux aarch64 in a container, as a non-root user.
Why I think it is a problem
The documentation for run says it "launches a Python script or module" and gives python -m profiling.sampling run script.py as the way to profile a script, with nothing about the script seeing a different sys.path than it would under python script.py. A script that works on its own and fails under the profiler is a surprise, and for the usual layout of a small program, a script with a helper module or package beside it, it means the profiler can only be run from inside that directory.
Two smaller differences show in the same output, which may or may not matter: sys.argv[0] is made absolute (/home/tester/w/sub/where.py rather than sub/where.py), and the script runs with sys.path[0] set to the working directory even when that is not where the script is, which is the python -m rule applied to a script.
Where it comes from (from reading the source, not verified by me)
The agent's reading of Lib/profiling/sampling/_sync_coordinator.py: _setup_environment changes to the working directory and inserts it at the front of sys.path ("Add current directory to sys.path if not present (for module imports)"), which is right for -m but is done for scripts too. _execute_script then makes the script path absolute, sets sys.argv, and executes the compiled source with exec in a fresh __main__ module, without inserting os.path.dirname(script_path) into sys.path as the interpreter does for a script. A one-line insert of the script's directory at sys.path[0] in the script case looks like the fix, but I cannot say whether there is a reason it was left out.
CPython versions tested on:
3.15
Operating systems tested on:
Linux
Bug report
Background
I have been writing a set of guided workshops on Tachyon as a way of learning it (https://github.com/GrahamDumpleton/tachyon-workshops), using an AI agent to generate the workshop content. One of the example programs being profiled in a workshop is a script that imports a helper module from its own directory. The agent found that the script ran fine as
python dir/script.py, but failed with an import error aspython -m profiling.sampling run dir/script.py. I am reporting it in case it is unintended; it may be a known difference.What happens
When a script is run with the
runsubcommand from a directory other than the script's own,sys.path[0]is the current working directory, not the script's directory. A script that imports a module beside it works withpythonand fails withModuleNotFoundErrorunder the profiler.Running from inside the script's directory works, since the working directory and the script's directory are then the same:
Tested on 3.15.0rc2 (python-build-standalone build installed by uv, and the
python:3.15-rc-slimimage) on Linux aarch64 in a container, as a non-root user.Why I think it is a problem
The documentation for
runsays it "launches a Python script or module" and givespython -m profiling.sampling run script.pyas the way to profile a script, with nothing about the script seeing a differentsys.paththan it would underpython script.py. A script that works on its own and fails under the profiler is a surprise, and for the usual layout of a small program, a script with a helper module or package beside it, it means the profiler can only be run from inside that directory.Two smaller differences show in the same output, which may or may not matter:
sys.argv[0]is made absolute (/home/tester/w/sub/where.pyrather thansub/where.py), and the script runs withsys.path[0]set to the working directory even when that is not where the script is, which is thepython -mrule applied to a script.Where it comes from (from reading the source, not verified by me)
The agent's reading of
Lib/profiling/sampling/_sync_coordinator.py:_setup_environmentchanges to the working directory and inserts it at the front ofsys.path("Add current directory to sys.path if not present (for module imports)"), which is right for-mbut is done for scripts too._execute_scriptthen makes the script path absolute, setssys.argv, and executes the compiled source withexecin a fresh__main__module, without insertingos.path.dirname(script_path)intosys.pathas the interpreter does for a script. A one-line insert of the script's directory atsys.path[0]in the script case looks like the fix, but I cannot say whether there is a reason it was left out.CPython versions tested on:
3.15
Operating systems tested on:
Linux