os.spawnv

The spawn family = fork + exec in one call. spawnl/spawnle/spawnv/spawnve and P_WAIT/P_NOWAIT/P_NOWAITO exist on Unix and Windows; the PATH-searching spawnlp/spawnlpe/spawnvp/spawnvpe are not available on Windows; P_DETACH and P_OVERLAY are Windows only; posix_spawn, posix_spawnp and the POSIX_SPAWN_* constants are Unix only.

os functionPython 3 (all versions); posix_spawn 3.8+, POSIX_SPAWN_CLOSEFROM 3.13+
Common call
os.spawnv(os.P_WAIT, exe, [exe, arg1])
Returns
exit code (P_WAIT) or pid / handle (P_NOWAIT)
Replaces
subprocess.call / subprocess.Popen do the same, portably
Watch out
Windows does not quote arguments that contain spaces
os.spawnv(modemode — P_WAIT (wait, return the exit code), P_NOWAIT / P_NOWAITO (return at once with the pid), Windows only: P_DETACH (no console) and P_OVERLAY (replace the current process, like exec).type: int · required, pathpath — The program. Only the p-variants search PATH; the others need an absolute or relative path.type: str | PathLike · required, args) / spawnve / spawnvp / spawnvpe / spawnl(mode, pathpath — The program. Only the p-variants search PATH; the others need an absolute or relative path.type: str | PathLike · required, ...) / spawnle / spawnlp / spawnlpe / os.posix_spawn(path, argv, envenv — e-variants: the complete environment of the new process; keys and values must be str.type: Mapping[str, str] · required, *, file_actionsfile_actions — posix_spawn: fd operations done in the child before exec - (POSIX_SPAWN_OPEN, fd, path, flags, mode), (POSIX_SPAWN_CLOSE, fd), (POSIX_SPAWN_DUP2, fd, new_fd), (POSIX_SPAWN_CLOSEFROM, fd).type: sequence of tuples · default: None=None, ...)
→ int

Parameters

NameTypeRequiredDescription
modeintyesP_WAIT (wait, return the exit code), P_NOWAIT / P_NOWAITO (return at once with the pid), Windows only: P_DETACH (no console) and P_OVERLAY (replace the current process, like exec).
pathstr | PathLikeyesThe program. Only the p-variants search PATH; the others need an absolute or relative path.
argslist | tupleyesv-variants: the argv, starting with the program name (not empty). l-variants pass the same items as separate arguments.
envMapping[str, str]yese-variants: the complete environment of the new process; keys and values must be str.
file_actionssequence of tuplesno (None)posix_spawn: fd operations done in the child before exec - (POSIX_SPAWN_OPEN, fd, path, flags, mode), (POSIX_SPAWN_CLOSE, fd), (POSIX_SPAWN_DUP2, fd, new_fd), (POSIX_SPAWN_CLOSEFROM, fd).

Return value

int — spawn* with P_WAIT: the exit code, or -signal if a signal killed it (Unix). With P_NOWAIT: the process id (the process handle on Windows). posix_spawn/posix_spawnp: the child pid.

Common patterns

Run and wait (the docs example)
spawnlp and spawnvpe here are equivalent; both search PATH, so Unix only.
import os
os.spawnlp(os.P_WAIT, 'cp', 'cp', 'index.html', '/dev/null')
L = ['cp', 'index.html', '/dev/null']
os.spawnvpe(os.P_WAIT, 'cp', L, os.environ)
Start in the background, wait later
P_NOWAIT returns the pid (a handle on Windows) that waitpid accepts.
import os, sys
pid = os.spawnv(os.P_NOWAIT, sys.executable, [sys.executable, 'worker.py'])
# ... do other work ...
_, status = os.waitpid(pid, 0)
exit_code = os.waitstatus_to_exitcode(status)
posix_spawn with output redirected to a file
file_actions open log.txt as fd 1 in the child before the program starts.
import os
actions = [(os.POSIX_SPAWN_OPEN, 1, 'log.txt', os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644)]
pid = os.posix_spawnp('ls', ['ls', '-l'], os.environ, file_actions=actions)
os.waitpid(pid, 0)
The modern replacement
subprocess.run covers P_WAIT; subprocess.Popen covers P_NOWAIT.
import subprocess, sys
exit_code = subprocess.run([sys.executable, 'worker.py']).returncode
proc = subprocess.Popen([sys.executable, 'worker.py'])
proc.wait()

Examples

1. P_WAIT returns the exit code
import os, sys os.spawnv(os.P_WAIT, sys.executable, [sys.executable, '-c', 'raise(SystemExit(4))'])
Returns
4
2. The l form: arguments one by one
import os, sys os.spawnl(os.P_WAIT, sys.executable, sys.executable, '-c', 'raise(SystemExit(4))')
Returns
4
3. P_NOWAIT, then waitpid
import os, sys pid = os.spawnv(os.P_NOWAIT, sys.executable, [sys.executable, '-c', 'raise(SystemExit(7))']) _, status = os.waitpid(pid, 0) os.waitstatus_to_exitcode(status)
Returns
7
4. The portable mode values
import os (os.P_WAIT, os.P_NOWAIT)
Returns
(0, 1)
5. args must not be empty
import os, sys try: os.spawnv(os.P_WAIT, sys.executable, []) except ValueError as e: result = type(e).__name__ result
Returns
'ValueError'
6. args must be a list or tuple
import os, sys try: os.spawnv(os.P_WAIT, sys.executable, sys.executable) except TypeError as e: result = type(e).__name__ result
Returns
'TypeError'
7. Same thing with subprocess
import subprocess, sys subprocess.call([sys.executable, '-c', 'raise SystemExit(4)'])
Returns
4

Pitfalls

1. Arguments with spaces on Windows
On Windows spawn* builds the child's command line without quoting, so an argument containing a space arrives split in two (in our test, '-c', 'raise SystemExit(4)' made the child run just raise). subprocess quotes each argument properly. That is why the examples here use 'raise(SystemExit(4))'.
joined as is
' '.join(['-c', 'raise SystemExit(4)'])
'-c raise SystemExit(4)'
quoted like subprocess
import subprocess
subprocess.list2cmdline(['-c', 'raise SystemExit(4)'])
'-c "raise SystemExit(4)"'
2. Reading P_NOWAIT as an exit code
With P_NOWAIT the return value is a process id (or handle), not a result. Wait for the process to get the exit code - on Unix an un-waited child also stays a zombie.
compare the pid
import os, sys
rc = os.spawnv(os.P_NOWAIT, sys.executable, [sys.executable, '-c', 'raise(SystemExit(7))'])
os.waitpid(rc, 0)
rc == 7
False
waitpid
import os, sys
pid = os.spawnv(os.P_NOWAIT, sys.executable, [sys.executable, '-c', 'raise(SystemExit(7))'])
_, status = os.waitpid(pid, 0)
os.waitstatus_to_exitcode(status) == 7
True

When to use

Use it
  • Porting old code that already uses spawn*
  • posix_spawn: a fast fork-free start of a program on Unix with simple fd setup
Reach for something else
  • New code → subprocess.run / subprocess.Popen (the docs recommend it)
  • Arguments with spaces on Windows → subprocess, which quotes them
  • spawnle / spawnve on Windows → the docs call them not thread-safe there; use subprocess

Notes

CPython impl
On Windows spawnv/spawnve are C functions (Modules/posixmodule.c, _wspawnv / _wspawnve). On Unix the whole spawn* family is Python code in Lib/os.py: fork(), then exec in the child, then waitpid + waitstatus_to_exitcode in the parent for P_WAIT.
Availability
spawnl, spawnle, spawnv, spawnve, P_WAIT, P_NOWAIT, P_NOWAITO: Unix, Windows (not WASI, Android or iOS). spawnlp, spawnlpe, spawnvp, spawnvpe: not on Windows. P_DETACH, P_OVERLAY: Windows only. posix_spawn, posix_spawnp, POSIX_SPAWN_*: Unix only; POSIX_SPAWN_CLOSEFROM (3.13) only where the C library has posix_spawn_file_actions_addclosefrom_np().
Missing program
On Unix a program that cannot be executed gives exit code 127 (the forked child fails to exec and calls os._exit(127)); on Windows spawnv raises FileNotFoundError instead.
Constant values
P_WAIT is 0 and P_NOWAIT is 1 on Windows and Linux. P_NOWAITO is 3 on Windows but 1 (the same as P_NOWAIT) on Linux; P_DETACH is 4 and P_OVERLAY 2 on Windows.
Windows crash
In our test on Windows CPython 3.13.3, os.spawnve and os.spawnle crashed the interpreter with an access violation. subprocess.run(..., env=...) does the same job safely.
posix_spawn
env may be None (3.13+) to inherit the current environment. Keyword options setpgroup, resetids, setsid, setsigmask, setsigdef and scheduler map to the C POSIX_SPAWN_* flags.

FAQ

Starting real processes cannot run in the page, and the family is split by platform: spawnl/spawnv/spawnle/spawnve work on Unix and Windows, the PATH-searching p-variants are not available on Windows, P_DETACH/P_OVERLAY are Windows only and posix_spawn is Unix only. The examples use only the parts that behave the same on Windows and Linux, with a Python child.