from the console: > py -3.7 or any other not installed Python version gives: Requested Python version (3.7) not installed However, when the launcher is executed from python via subprocess.Popen: >>>import subprocess >>>p=subprocess.Popen(['py', '-3.7'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) >>>p.communicate() (b'', b'') the error message is not accessible. (Error messages from any successfully launched Python interpreter are available through p.stderr though.)
The error() function in PC/launcher.c should call exit(rc) instead of ExitProcess(rc). This allows the CRT to terminate properly and flush the stderr FILE stream. With this change it works as expected: >>> import subprocess >>> p = subprocess.Popen(r'amd64\py_d -3.7', stderr=subprocess.PIPE) >>> p.stderr.read() b'Requested Python version (3.7) not installed\r\n'
> The error() function in PC/launcher.c should call exit(rc) instead of ExitProcess(rc). This allows the CRT to terminate properly and flush the stderr FILE stream. Interesting. Why do you need to flush stderr? I would have expected it to be unbuffered in the first place. What's the usecase/advantage of calling ExitProcess then? Sorry, if that does not really belong here and just shows my limited knowledge of C.
ExitProcess is a system API and exit is the C runtime API. The C runtime is doing the buffering, so the system doesn't know about it and can't flush if you terminate through that API. The exit function should, or we could explicitly flush the buffer after writing to it.
Patch 2 additionally modifies run_child to call exit() instead of ExitProcess. For example: >>> import os, subprocess >>> os.environ['PYLAUNCH_DEBUG'] = '1' >>> p = subprocess.Popen(r'py -3 -c ""', stderr=subprocess.PIPE, stdout=subprocess.PIPE) >>> p.stderr.read() b'' Patched: >>> p = subprocess.Popen(r'amd64\py_d -3 -c ""', stderr=subprocess.PIPE, stdout=subprocess.PIPE) >>> p.stderr.readlines()[-1] b'child process exit code: 0\r\n' For good measure I also added a call to setvbuf to disable buffering stderr.