Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

sys.stderr is None when running generated exe in bash on Windows #168

Closed
charlieman opened this issue Apr 13, 2022 · 16 comments
Closed

sys.stderr is None when running generated exe in bash on Windows #168

charlieman opened this issue Apr 13, 2022 · 16 comments

Comments

@charlieman
Copy link

charlieman commented Apr 13, 2022

Describe the bug
On Windows using distlib version 0.3.4 and using a bash terminal, when I run an exe file generated by distlib, sys.stderr is set to None. Running the same exe from cmd or powershell works as expected. Generating the exe with distlib 0.3.3 also works as expected.

This was first reported in IPython ipython/ipython#13509. IPython works fine when running python -m IPython or when running ipython.exe from other shells like cmd or powershell. This was later fixed because pip downgraded distlib here but I thought to report it here for visibility.

I'm including a small example that prints sys.stdout and sys.stderr for comparison.

To Reproduce
Steps to reproduce the behavior:

  1. Download and unzip this file: bugexe.zip
  2. Go to that folder and create a venv: python -m venv .venv
  3. Activate the venv: source .venv/Scripts/activate
  4. Install distlib 0.3.4 python -m pip install distlib==0.3.4
  5. Run python make.py to create an executable called foo.exe
  6. From bash run: PYTHONPATH=. ./foo.exe It prints this:
sys.stdout=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
sys.stderr=None
  1. For comparison, run from cmd: cmd /V /C "set "PYTHONPATH=." && foo.exe"
  2. Or powershell: powershell -Command { $env:PYTHONPATH="."; .\foo.exe }

Expected behavior
It should print this:

sys.stdout=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
sys.stderr=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>

Environment

  • OS: Windows 10
  • Using bash version 4.4.23(2)-release (x86_64-pc-msys) that comes bundled with git for windows
  • Version of this library: 0.3.4

Additional information
This problem doesn't happen with distlib version 0.3.3

@vsajip
Copy link
Collaborator

vsajip commented May 6, 2022

I think this is out of date. I don't get this with the repo as at (for example) this commit. So I aim to close this, unless you can reproduce it with the latest distlib version in this repo. (You can do pip install -e /c/path/to/clone from a cloned repo to verify, and if it works, you can close this.)

@vsajip
Copy link
Collaborator

vsajip commented May 9, 2022

Should be fixed in 8d2acb5.

@vsajip vsajip closed this as completed May 9, 2022
@zackees
Copy link

zackees commented Dec 2, 2022

Is this bug causing this issue?

pypa/pip#11632

I'm getting the exact same thing, a sys.stderr == None issue which causes the sys.stderr to fail when input is prompted during pip uninstall mypackage while inside a virtual environment.

@zackees
Copy link

zackees commented Dec 2, 2022

Confirmed, a change since 0.3.3 is causing this issue. I resolve it by replacing the binaries in my global python with the ones from the 0.3.3 version:

  1. I downloaded distlib 0.3.3 zip file
  2. I unzipped the t32.exe, t64.exe, t64-arm.exe, w32.exe, w64.exe, w64-arm.exe into C:\Users\niteris\AppData\Local\Programs\Python\Python310\Lib\site-packages\distlib
  3. Then I repeat the repro steps
  4. Now the pip uninstall works without a crash. I also confirmed now that the stderr pipe is set and in text mode utf-8. I am getting the following prompt as expected:

pypa/pip#11630 (comment)

Repro steps to this issue can be found here:

  1. Start in git-bash
  2. virtualenv -p python3 venv
  3. . venv/Scripts/activate
  4. pip install keyvalue_sqlite
  5. ERROR STEP: pip uninstall keyvalue_sqlite, outputs "input(): lost sys.stderr"

This is the possible culprit:

static BOOL
make_handle_inheritable(HANDLE handle)
{
    DWORD file_type = GetFileType(handle);
    // Ignore an invalid handle, non-file object type, unsupported file type,
    // or a console file prior to Windows 8.
    if (file_type == FILE_TYPE_UNKNOWN ||
        (file_type == FILE_TYPE_CHAR && ((ULONG_PTR)handle & 3))) {
        return TRUE;
    }

    return SetHandleInformation(handle, HANDLE_FLAG_INHERIT,
        HANDLE_FLAG_INHERIT);
}

8d2acb5#diff-e2ab17b255bd58eedd164c4013e70168ee5441e9a93ca15a09baa2d6d912d9c0R530

I suspect that what is happening is that the file_type while in git-bash is unhandled and the catch all return TRUE; path is being hit, causing sys.stderr to become None.

@eryksun
Copy link

eryksun commented Dec 2, 2022

suspect that what is happening is that the file_type while in git-bash is unhandled and the catch all return TRUE; path is being hit, causing sys.stderr to become None.

Unless something has changed recently, git-bash runs bash attached to a mintty terminal that uses named pipes for I/O, with the file type FILE_TYPE_PIPE. Anyway, the standard I/O handles are already marked as inheritable in this case, and most cases. I know of one corner case1 in which make_handle_inheritable() is sometimes required.

I was unable to reproduce CPython issue 99937 using git-bash and Python 3.10.8, so I still don't have anything reproducible that I can dig into.

Whatever is causing the problem, my guess is that it's ultimately due to the fact that bash spawns a process using the same handle value for hStdOut and hStdErr. They should be different handles, even if they reference the same kernel file. Closing stdout shouldn't have the side effect of closing stderr. bash is misbehaving.

Footnotes

  1. If a console app is spawned without inheriting handles from the parent process; without the startup flag STARTF_USESTDHANDLES; and without any of the creation flags DETACHED_PROCESS, CREATE_NEW_CONSOLE, or CREATE_NO_WINDOW; then the system implicitly duplicates the parent's standard handles to the child, without changing the attributes. Thus if a standard handle isn't inheritable in the parent, then the duplicated handle won't be inheritable as well.

@zackees
Copy link

zackees commented Dec 2, 2022

It's not that stderr is broken, it's that it's set to None and being reported as broken from pip uninstall

@zackees
Copy link

zackees commented Dec 2, 2022

Can someone point me to build instructions for distlib? I'm a C++ and python programmer but have never built a python+cpp project before. I'm going to take a stab and figure out what's causing this.

@vsajip
Copy link
Collaborator

vsajip commented Dec 2, 2022

distlib is a pure-Python project, but the launcher executables it uses are maintained in this project. The files in the PC folder here are just copies of the files there. That's a vanilla C project, BTW.

I'm going to take a stab and figure out what's causing this.

Thanks.

@eryksun
Copy link

eryksun commented Dec 2, 2022

It may be something unusual in your configuration, or perhaps mine, but the combination of mintty, bash, Python 3.10.8, and pip 22.2.2 (distlib 0.3.5) work fine for me. sys.stderr is not None when Python is invoked from the "pip.exe" launcher.

@zackees
Copy link

zackees commented Dec 2, 2022

I'm blocked on building this, it says it needs Visual Studio 10, which isn't available. I downloaded the Visual Studio Shell 10 and it says:
image

Does any one have the workaround?

@vsajip
Copy link
Collaborator

vsajip commented Dec 2, 2022

You should be able to use a newer version of Visual Studio. For example, I built the ARM version using VS 2019. I stick with VS2010 because for some reason the executables built with VS2019 are a lot larger.

@zackees
Copy link

zackees commented Dec 2, 2022

Okay that built.

It creates an output like CLISimpleLauncher.exe. Do I rename this to t64..exe ?

@vsajip
Copy link
Collaborator

vsajip commented Dec 2, 2022

If you do a Release batch build, it should create with those names.

@zackees
Copy link

zackees commented Dec 2, 2022

Thanks, that worked. I changed build.cmd to use visual studio 19 and it just builds.

@zackees
Copy link

zackees commented Dec 2, 2022

Now I can't reproduce the issue after I did a fresh reinstall (and removal of all the files) of python 3.10.8

@eryksun
Copy link

eryksun commented Dec 2, 2022

my guess is that it's ultimately due to the fact that bash spawns a process using the same handle value for hStdOut and hStdErr. They should be different handles, even if they reference the same kernel file. Closing stdout shouldn't have the side effect of closing stderr. bash is misbehaving.

As it happens, the C runtime's _close() function protects against this by skipping CloseHandle() for either _close(1) or _close(2) if _get_osfhandle(1) == _get_osfhandle(2). Because Python's I/O stack uses CRT file descriptors and _close() instead of OS handles (except for sockets), it generally isn't a problem if stdout and stderr have the same OS file handle. However, a child process that works with OS handles directly may inadvertently close stderr if it closes stdout to redirect it. The launcher could preempt this by replacing STD_ERROR_HANDLE with a duplicate handle in this case, and only in this case. There's no reason to duplicate all of the standard handles all of the time, which just leads to other problems by leaking pipe handles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants