os.execv

exec does not start a child - it swaps the current program for a new one, so no line after a successful exec ever runs. Available on Unix and Windows (not WASI, Android or iOS); on Unix the new program keeps the same process id, on Windows it runs as a new process.

os functionPython 3 (all versions); get_exec_path 3.2+, path-like objects 3.6+
Common call
os.execv(sys.executable, [sys.executable] + sys.argv)
Returns
never returns on success
Replaces
subprocess.run when you want to come back
Watch out
args[0] is the program name, not the first argument
os.execv(pathpath — execv/execve/execl/execle: the program file. PATH is not searched; a relative path needs at least one slash, even on Windows.type: str | PathLike · required, args) / execve(path, argsargs — v-variants: the full argv. args[0] is passed as the program name; it must not be empty.type: list | tuple · required, env) / execvp(file, args) / execvpe(file, argsargs — v-variants: the full argv. args[0] is passed as the program name; it must not be empty.type: list | tuple · required, env) / execl(path, arg0, ...) / execle / execlp / execlpe / os.get_exec_path(env=None)
→ never returns | list[str]

Parameters

NameTypeRequiredDescription
pathstr | PathLikeyesexecv/execve/execl/execle: the program file. PATH is not searched; a relative path needs at least one slash, even on Windows.
filestr | PathLikeyesThe p-variants: a program name looked up in PATH (from env when one is given).
argslist | tupleyesv-variants: the full argv. args[0] is passed as the program name; it must not be empty.
arg0, arg1, ...stryesl-variants: the same argv written as separate arguments.
envMapping[str, str]yese-variants: the complete environment of the new program (it does not inherit os.environ). Keys and values must be str.

Return value

never returns | list[str] — The exec* functions do not return - the new program takes over (errors raise OSError instead). get_exec_path() returns the list of PATH directories.

Common patterns

Restart the current script
Typical after a self-update: same interpreter, same arguments. Flush first - buffers are not flushed.
import os, sys
sys.stdout.flush()
os.execv(sys.executable, [sys.executable] + sys.argv)
Hand over to another program (shell-style exec)
A launcher that sets things up and then becomes the real program.
import os
env = {**os.environ, 'APP_MODE': 'production'}
os.execvpe('gunicorn', ['gunicorn', 'app:server'], env)
Where would execvp look?
get_exec_path splits PATH the same way the p-variants do.
import os
for folder in os.get_exec_path():
    print(folder)
fork + exec (Unix)
The classic way to start a child program; subprocess does this for you.
import os
pid = os.fork()
if pid == 0:
    try:
        os.execlp('ls', 'ls', '-l')
    finally:
        os._exit(127)
os.waitpid(pid, 0)

Examples

1. Directories a p-variant searches
import os os.get_exec_path({'PATH': os.pathsep.join(['tools', 'bin'])})
Returns
['tools', 'bin']
2. Empty PATH
import os os.get_exec_path({'PATH': ''})
Returns
['']
3. Missing program: OSError, and exec returns
import subprocess, sys code = 'import os\ntry:\n os.execv("no_such_program", ["no_such_program"])\nexcept OSError as e:\n print(type(e).__name__, e.errno)' subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
Returns
'FileNotFoundError 2\n'
4. execvp searches PATH, same error
import subprocess, sys code = 'import os\ntry:\n os.execvp("no_such_program_xyz", ["no_such_program_xyz"])\nexcept OSError as e:\n print(type(e).__name__, e.errno)' subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
Returns
'FileNotFoundError 2\n'
5. args must not be empty
import subprocess, sys code = 'import os, sys\ntry:\n os.execv(sys.executable, [])\nexcept ValueError as e:\n print(e)' subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
Returns
'execv() arg 2 must not be empty\n'
6. args[0] must not be empty either
import subprocess, sys code = 'import os, sys\ntry:\n os.execv(sys.executable, [""])\nexcept ValueError as e:\n print(e)' subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
Returns
'execv() arg 2 first element cannot be empty\n'
7. Run and come back: subprocess with env
import os, subprocess, sys env = {**os.environ, 'GREETING': 'hi'} r = subprocess.run([sys.executable, '-c', 'import os; print(os.environ["GREETING"])'], env=env, capture_output=True, text=True) r.stdout
Returns
'hi\n'

Pitfalls

1. Passing the command line as one string
The v-variants want a list or tuple of separate arguments; a string is rejected. Split it first (shlex.split follows POSIX shell rules).
a string
import subprocess, sys
code = 'import os, sys\ntry:\n    os.execv(sys.executable, "python -V")\nexcept TypeError as e:\n    print(e)'
subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
'execv() arg 2 must be a tuple or list\n'
shlex.split
import shlex
shlex.split('python -V')
['python', '-V']
2. Non-string values in env
Every key and value in the env mapping of execve/execle/execvpe/execlpe must be a string.
int value
import subprocess, sys
code = 'import os, sys\ntry:\n    os.execve(sys.executable, [sys.executable], {"DEBUG": 1})\nexcept TypeError as e:\n    print(e)'
subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
'expected str, bytes or os.PathLike object, not int\n'
convert to str
settings = {'DEBUG': 1, 'WORKERS': 4}
{k: str(v) for k, v in settings.items()}
{'DEBUG': '1', 'WORKERS': '4'}

When to use

Use it
  • A launcher or wrapper script whose last step is to become the real program
  • Restarting the current Python program in place (Unix keeps the pid)
  • The exec half of a manual fork + exec
Reach for something else
  • Running a program and continuing afterwards → subprocess.run
  • Windows, if anything waits for your process: the original process ends and the program continues as a new one
  • Arguments with spaces on Windows: they are not quoted for you

Notes

CPython impl
execv/execve live in Modules/posixmodule.c (execv() / execve() on Unix, _wexecv / _wexecve on Windows). The l-, p- and pe-variants are Python wrappers in Lib/os.py; execvp/execvpe walk get_exec_path() and call execv/execve per directory.
Availability
exec*: Unix, Windows, not WASI, Android or iOS. get_exec_path: everywhere.
Naming
l = arguments listed one by one, v = arguments in a list (vector), p = search PATH, e = pass an explicit environment. The letters combine: execvpe = list + PATH + env.
Buffers
The process is replaced immediately: open files and stdout are not flushed. Call sys.stdout.flush() (or os.fsync on files) before exec.
Windows
In our test on Windows CPython 3.13 the exec'd program got a different process id and a parent waiting with subprocess.run returned at once, while on Linux the pid stayed the same and the wait lasted until the new program ended. Arguments containing spaces were split, because they are not quoted.
execve with fd
On some platforms execve accepts an open file descriptor as path (see os.supports_fd); elsewhere that raises NotImplementedError.

FAQ

A successful exec replaces the process that calls it, so it cannot run inside the page. The exec functions exist on Unix and Windows (not WASI, Android or iOS), but behave differently: Unix keeps the process id, Windows starts a new process. The examples therefore only run exec calls that fail before replacing anything, inside a separate Python child.