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

stdout and stderr are non-blocking #444

Closed
glyph opened this issue May 7, 2022 · 10 comments
Closed

stdout and stderr are non-blocking #444

glyph opened this issue May 7, 2022 · 10 comments

Comments

@glyph
Copy link
Contributor

glyph commented May 7, 2022

If you put this code very close to the beginning of your python code in py2app:

import fcntl, os
print(fcntl.fcntl(1, fcntl.F_GETFL) & os.O_NONBLOCK, fcntl.fcntl(2, fcntl.F_GETFL) & os.O_NONBLOCK))

and run it in a terminal, you will see 4 4, indicating that the stdout and stderr streams have been set to non-blocking mode for some reason. This means (among other things) tracebacks will be irritatingly truncated when debugging in this way. It's easy enough to fix—just for fd in [1, 2]: fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)—but this is annoying out-of-the-box behavior and very obscure.

Is this something that AppKit or LaunchServices are doing on one's behalf, somehow? I don't know how they'd get involved before NSApplicationMain when you're just running the app as a binary in the terminal; is it behavior in the py2app stub?

@ronaldoussoren
Copy link
Owner

It could be behaviour in the py2app stub, but I don't explicitly set nonblocking mode there.

I don't get this behaviour with the project in examples/tkinter/hello_tk in the py2app source tree (macOS 12, python 3.10). That's using py2app from the repository, but that has no changes to the stub executable.

The option redirect_stdout_to_asl does affect stdout and stderr, but that option is off by default and redirects both streams to Apple's logging system (and has the side effect of no longer using the stdout stream).

@glyph
Copy link
Contributor Author

glyph commented May 14, 2022

I'm using Twisted in this app and it's possible that something in there is doing this — although it shouldn't be, that's only supposed to happen with StandardIO (and I'm not using that).

I'll try to create a minimal reproducer.

@glyph
Copy link
Contributor Author

glyph commented May 18, 2022

OK, this gets even weirder. I definitely narrowed it down to a minimal reproducer and it was definitely not what I thought. The bug is in py2app (or maybe distutils?) but not in anything runtime-related.

If you make a shell script that runs python setup.py py2app, that sets stdout to be non-blocking. I set up a repo here https://github.com/glyph/py2app-nonblocking-example for a minimal reproducer. If you just build the app and run it in your terminal, no problem (i.e. "0 0") but if you run the included shell script, it's broken ("4 4"). Unless you modify python setup.py py2app to be python setup.py py2app | cat which interposes something other than a tty onto stdout and makes the non-blocking-ness not stick around.

This doesn't happen with, e.g. python setup.py egg_info so it's not trivially distutils's fault.

I think my shell (zsh) is resetting the blocking state of the terminal on every interactive prompt, I think, which is why this doesn't happen interactively.

@glyph
Copy link
Contributor Author

glyph commented May 21, 2022

Weirder still. This affects builds themselves. The output from --alias is short enough that I don't get this, but a regular python setup.py py2app on https://github.com/glyph/Pomodouroboros results in this crash:

BlockingIOError: [Errno 35] write could not complete without blocking

but python setup.py py2app | cat works properly.

@ronaldoussoren
Copy link
Owner

This is very weird indeed, but finding the root cause might help fix other build issues. Py2app contains some retry loops for subcommands that fail, and IIRC at least some of them were added due to similar errors.

Are you on an M1 based system, or at least use a Python build with arm64 support (such as the 3.10 installers on Python.org)?

ronaldoussoren added a commit that referenced this issue May 22, 2022
For some reason the ibtool(1) command sets the std* streams
to non-blocking when compiling nib files. Add  a context manager
to reset the blocking status for these streams and use
that will all invocations of xcode tools (not just ibtool).
@ronaldoussoren
Copy link
Owner

ronaldoussoren commented May 22, 2022

Turns out this is a mis-feature of the ibtools(1) command used to compile NIB/XIB files. The changeset above fixes this and removes an older workaround for broken builds.

I'm not closing this issue yet because I'm thinking about creating a branch with a hot fix in the 0.28 release. I'm not comfortable yet about releasing from the tip of the tree, I'm partway through code cleanup and am not 100% sure that a release would be problem free.

And finally: Thanks for the reproducer, that made it a lot easier to debug the issue.

@ronaldoussoren
Copy link
Owner

Sigh.

For some reason my fixed worked for some time, but working on a back port somehow broke things again.

To be continued...

@ronaldoussoren
Copy link
Owner

323fe55, 43422ef, d9b37ed

The previous "sigh" was of my own doing, the code that resets blocking state was buggy.

Both master and v0.28-branch seem to work correctly now. Current plan is to push a release from the v0.28-branch later today.

@ronaldoussoren
Copy link
Owner

This should be fixed in py2app 0.28.2 which is on PyPI.

@glyph
Copy link
Contributor Author

glyph commented Feb 17, 2023

@ronaldoussoren thank you for fixing this! I independently discovered the ibtool weirdness and was coming back here excited to report my findings :).

glyph added a commit to glyph/Pomodouroboros that referenced this issue Feb 17, 2023
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

2 participants