Skip to content

os.execvp on windows doesn't wait for the executed program to finish and doesn't report it's exit code #101191

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
snoopyjc opened this issue Jan 20, 2023 · 5 comments
Labels
OS-windows type-bug An unexpected behavior, bug, or error

Comments

@snoopyjc
Copy link

Bug report

os.execvp on windows doesn't wait for the executed program to finish and doesn't report it's exit code. Code:

import os
import sys

if len(sys.argv) > 1 and sys.argv[1] == "my_argument":
    sys.stderr.write("my_argument detected. Exiting with code 1.\n")
    sys.exit(1)
else:
    sys.stderr.write("no argument detected. Calling os.execvp\n")
    os.execvp(sys.executable, [sys.executable] + sys.argv + ["my_argument"])

bat file:

@echo off
python exec.py
if %errorlevel% NEQ 0 (
    echo The script gave exit code %errorlevel%
) else (
    echo Error: The script exited with exit code 0
)

Output from command prompt:

C:\pythonizer\play>exec.bat
no argument detected. Calling os.execvp
Error: The script exited with exit code 0

C:\pythonizer\play>my_argument detected. Exiting with code 1.

Note the command prompt prompted me for a new command before the exec'd process wrote out it's message.

Your environment

  • CPython versions tested on: 3.10.6
  • Operating system and architecture: Windows 10
@snoopyjc snoopyjc added the type-bug An unexpected behavior, bug, or error label Jan 20, 2023
@eryksun
Copy link
Contributor

eryksun commented Jan 20, 2023

The POSIX exec*() functions cannot be implemented on Windows since the OS doesn't support replacing the process image. At most, the exec*() functions can be crudely emulated. The C runtime library on Windows does the minimum that remains generally safe. It spawns the new process and exits. Python, for its part, simply calls C _wexecv() and _wexecve().

If we were to implement our own versions of execv() and execve() that spawn a child process and wait for it to exit, we'd have to take some defensive steps. We would have to either forcefully terminate or suspend all other threads in the current process. We'd have to walk a process snapshot to close unnecessary file handles in order to avoid problems with closing pipes or problems with the share mode of files (this includes having to change the working directory to the user's temp directory). That's not even considering the problems with other inherited kernel objects (e.g. a mutex). Also, the spawned process should be added to a kill-on-close, silent-breakaway job object.

This would still be very far from truly emulating an exec*() that replaces the image of the current process. We can't support any scenario with tight coupling to the parent process or some other ancestor process. For example, an ancestor of the current process might expect to be able to use a handle for the current process, or its PID, to get or set various information or otherwise modify the current process. This can include any of the following functions, depending on the context:

Base API

Handle API

Process and Thread API

Memory API

Process Snapshotting API

Process Status API

Tool Help API

@snoopyjc
Copy link
Author

Thank you for the detailed analysis. I looked into how perl handles exec on windows, and it calls _execvp - can Python just do the same thing as perl? https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/execvp-wexecvp?view=vs-2019

@eryksun
Copy link
Contributor

eryksun commented Jan 22, 2023

All of the C runtime's _[w]exec*() functions are implemented to spawn the executable and then exit the current process. They do not wait on the spawned process.

Python uses _wexecv() to implement os.execv(), and it uses _wexecve() to implement os.execve(). It defines the other os.exec*() functions in terms of those two -- including os.execvp(), os.execvpe(), os.execl(), os.execle(), os.execlp(), and os.execlpe().

@venthur
Copy link

venthur commented Jun 19, 2023

Would it make sense to put a big warning in the corresponding docs?

Currently it says:

These functions all execute a new program, replacing the current process; they do not return. On Unix, the new executable is loaded into the current process, and will have the same process id as the caller. Errors will be reported as OSError exceptions.

[...]

Availability: Unix, Windows, not Emscripten, not WASI.

Maybe one could clarify that it does not work as described on Windows (and point to Popen as the next best alternative)

@jiasli
Copy link
Contributor

jiasli commented Aug 9, 2023

It seems this issue has been reported before in #63323.

semgrep-ci bot pushed a commit to semgrep/semgrep that referenced this issue Mar 4, 2025
…mgrep/semgrep-proprietary#3171)

On Windows, the execvp function doesn't replace the current process with
the new process. The function spawns a new process in the background and
control is returned to the caller [1][2], breaking the CLI
interactivity.

This commit switches to spawning another process both in the Python and 
OCaml code for better CLI interactivity on Windows.

Unix.execv's documentation[3] recommends using Unix.create_process, but
this commit uses Bos.OS.Cmd.run since similar approaches are already
being used to spawn other processes and capture their output, etc.

[1] - https://bugs.python.org/issue9148
[2] -
python/cpython#101191 (comment)
[3] - https://ocaml.org/manual/5.2/api/Unix.html#VALexecv

synced from Pro 539bd07bda6a46c99d73002b18e273d0cabbe03d
semgrep-ci bot pushed a commit to semgrep/semgrep that referenced this issue Mar 4, 2025
…mgrep/semgrep-proprietary#3171)

On Windows, the execvp function doesn't replace the current process with
the new process. The function spawns a new process in the background and
control is returned to the caller [1][2], breaking the CLI
interactivity.

This commit switches to spawning another process both in the Python and 
OCaml code for better CLI interactivity on Windows.

Unix.execv's documentation[3] recommends using Unix.create_process, but
this commit uses Bos.OS.Cmd.run since similar approaches are already
being used to spawn other processes and capture their output, etc.

[1] - https://bugs.python.org/issue9148
[2] -
python/cpython#101191 (comment)
[3] - https://ocaml.org/manual/5.2/api/Unix.html#VALexecv

synced from Pro 539bd07bda6a46c99d73002b18e273d0cabbe03d
semgrep-ci bot pushed a commit to semgrep/semgrep that referenced this issue Mar 5, 2025
…mgrep/semgrep-proprietary#3171)

On Windows, the execvp function doesn't replace the current process with
the new process. The function spawns a new process in the background and
control is returned to the caller [1][2], breaking the CLI
interactivity.

This commit switches to spawning another process both in the Python and 
OCaml code for better CLI interactivity on Windows.

Unix.execv's documentation[3] recommends using Unix.create_process, but
this commit uses Bos.OS.Cmd.run since similar approaches are already
being used to spawn other processes and capture their output, etc.

[1] - https://bugs.python.org/issue9148
[2] -
python/cpython#101191 (comment)
[3] - https://ocaml.org/manual/5.2/api/Unix.html#VALexecv

synced from Pro 539bd07bda6a46c99d73002b18e273d0cabbe03d
ajbt200128 pushed a commit to semgrep/semgrep that referenced this issue Mar 5, 2025
…mgrep/semgrep-proprietary#3171)

On Windows, the execvp function doesn't replace the current process with
the new process. The function spawns a new process in the background and
control is returned to the caller [1][2], breaking the CLI
interactivity.

This commit switches to spawning another process both in the Python and 
OCaml code for better CLI interactivity on Windows.

Unix.execv's documentation[3] recommends using Unix.create_process, but
this commit uses Bos.OS.Cmd.run since similar approaches are already
being used to spawn other processes and capture their output, etc.

[1] - https://bugs.python.org/issue9148
[2] -
python/cpython#101191 (comment)
[3] - https://ocaml.org/manual/5.2/api/Unix.html#VALexecv

synced from Pro 539bd07bda6a46c99d73002b18e273d0cabbe03d
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
OS-windows type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants