-
-
Couldn't load subscription status.
- Fork 33.3k
GH-103929: handle long input lines with PyOS_InputHook #103931
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
base: main
Are you sure you want to change the base?
Conversation
a63c908 to
1e6fc5b
Compare
The usual way to test a C API call directly is add a method to the |
|
@brandtbucher: Maybe you have enough context to review this since you've been in readline-adjacent things lately? |
|
I'll take a look |
1e6fc5b to
e3759ab
Compare
|
rebased to handle conflicts with the sub-processor work. |
e3759ab to
e69cd68
Compare
|
Rebased again. |
e69cd68 to
281588a
Compare
281588a to
5f76a95
Compare
|
@brandtbucher did you ever get a chance to review this one? |
5f76a95 to
c433296
Compare
|
@pablogsal, @ambv: If I understand correctly, users are more likely to hit this bug in 3.14 since the new REPL doesn't use readline. Should we try to get this into the next 3.14 beta? |
This fix doesn't apply to the new REPL, so I don't think how this PR could help here |
Got it. My bad. |
|
@pablogsal Does that mean the new REPL should/will not have this 100 char limit while |
|
The bug that is fixed is in the readline-replacement implementation that Python carries, not in the input hooks, so if there are bugs in the new repl with long lines, they will be different bugs ;) |
|
I'll take that as a solid maybe ;). Will report if I find similar long-line bugs with active input hooks. Update: re-reading, it seems you mean the bug remains, but that the new REPL has worked around it in another way. Those of us using input-based REPL's the still need this fix. |
|
This bug shows up not only with For example, running the commands in this script one-by-one will freeze Python after >>> import tkinter
>>> t = tkinter.Tk()
>>> short_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
>>> long_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
>>> >>> ^D(as @tacaswell mentioned, the bug only appears on Python without readline). |
If: - stdin is in line buffer mode - the readline module is not loaded - not on windows - a GUI toolkit has installd PyOS_InputHook Then, if the user enters lines longer than 98 characters into `input`: - user calls `input(...)` - user types/pastes a line longer than 98 characters + 1 newline - PyOS_InputHook returns - the first 99 characters are returned - the last character is not a new line so we go back to my_fgets - the PyOS_InputHook blocks because despite there being value in the buffer, stdin does not flag itself as ready to be read again - user hits enter again and to finish reading the input - the extra new line comes out the next time the user calls input This fixes this bug by passing the currently read number of characters to `my_fgets` to skip the input hook when reading out a long buffer. closes python#103929
c433296 to
1d13392
Compare
|
It would be nice to get this reviewed and merged. For now the only workaround is |
When stdin is unbuffered, then fgets reads directly from stdin and leaves any characters in stdin that do not fit in the fgets buffer. PyOS_InputHook will then see those remaining characters, and return. When stdin is buffered, then fgets reads the data from stdin into an internal buffer. This internal buffer is likely bigger than even the long input line, so it will read the full line into its internal buffer, leaving nothing in stdin. PyOS_InputHook then doesn't find anything in stdin, and will block waiting for new input. |
|
rebased to re-trigger CI. |
(meaning this sentence: "This function [PyOS_InputHook] should block until stdin is readable."). I have not seen it described in the documentation, but this is how PyOS_InputHook is used in practice (e.g. in the But, if so, then the current PR is not ideal. Once we start calling |
|
I think we need the inputhook inside of that Maybe it should be rephrased to:
This lets you run a loop like "run UI for 5ms, check stid, run UI for 5ms ...". This bit of code is here due to https://github.com/python/cpython/pull/7978/files which I put in with guidance from @zooba . |
This loop happens inside of the There is no need to have such a second loop on top of it outside of See also pull request #140147 which gets rid of the Windows-specific code in |
|
But does it happen inside the function for all function currently in the wild? I have a recent-ish survey here: #119843 (comment) |
In the main packages (matplotlib, PyQt5, PyQt6, PySide, and PyGTK), PyOS_InputHook points to a function that returns once data is available on stdin. PyGObject (which superseded PyGTK) does not use PyOS_InputHook. For wxPython: there was a discussion about it 16 years ago (see https://discuss.wxpython.org/t/interactive-usage-of-wxpython/32128/22, where I was making the same comments). But I did not find any (recent or old) version of wxPython that actually uses PyOS_InputHook, so I guess it was never implemented. Now there may be some module in the wild that assumes that PyOS_InputHook is called repeatedly. I didn't find such a module, but if it exists, its event loop will freeze with Python without readline support. |
If:
Then, if the user enters lines longer than 98 characters into
input:input(...)This fixes this bug by passing the currently read number of characters to
my_fgetsto skip the input hook when reading out a long buffer.closes #103929
Qustions: