Skip to content
Python Basics: Execute a Trusted Script with runpy

Python Basics: Execute a Trusted Script with runpy

October 27, 2026

A small test harness may need to execute a trusted script and inspect its resulting globals. runpy.run_path does that in a fresh namespace in the current process. This fixture supplies an input value and checks how the script’s name affects its main guard.

Prepare a scratch directory

Tested on Linux with CPython 3.12.14. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. Run Python without -O so the assertions remain enabled. Any generated fixture files live inside an automatically cleaned temporary directory.

Keep using the same terminal. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-runpy.py:

from pathlib import Path
from tempfile import TemporaryDirectory
import runpy

with TemporaryDirectory() as directory:
    source = Path(directory) / "calculate_report.py"
    source.write_text(
        'result = amount * 2\n'
        'ran_main = (__name__ == "__main__")\n', encoding="utf-8")
    inputs = {"amount": 7}
    ordinary = runpy.run_path(str(source), init_globals=inputs)
    assert ordinary["result"] == 14
    assert ordinary["ran_main"] is False
    assert "result" not in inputs
    print("Result:", ordinary["result"])
    print("Default run executed as main:", ordinary["ran_main"])
    main = runpy.run_path(str(source), init_globals=inputs, run_name="__main__")
    assert main["ran_main"] is True
    print("Explicit main name:", main["ran_main"])
    print("Input dictionary gained result:", "result" in inputs)

Run it:

python3 -B demonstrate-runpy.py

Observed output:

Result: 14
Default run executed as main: False
Explicit main name: True
Input dictionary gained result: False

Read the result

The input dictionary seeds the new namespace, and the returned dictionary contains the script’s result. run_name controls the special __name__ value, making the usual main-guard condition true in the second run. The original input dictionary is not extended with the script’s new bindings. The source is deliberately tiny and created by this program, so no third-party file is executed.

Apply it carefully

runpy is not a sandbox: executed code can read files, import modules, and change process state. Side effects can outlive the call. The initial globals are not a deep copy of every supplied object, so mutable values can still be shared. Execution temporarily adjusts interpreter state, which requires care in threaded programs. Use ordinary imports for reusable APIs and a subprocess when genuine process isolation is needed.

The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.

Clean up

Remove the source and then the empty practice directory:

rm -- demonstrate-runpy.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect the remaining files rather than deleting unknown contents.

Acknowledgements

This post was created with assistance from AI.

Last updated on