Closed joostkremers closed 2 years ago
I suspect this comes from the attempt to cancel the current command. Try M-: (python-mls-interrupt-quietly)
and see if your process hangs. Please also report the value of python-mls-continuation-prompt
in your shell buffer.
I suspect this comes from the attempt to cancel the current command. Try
M-: (python-mls-interrupt-quietly)
and see if your process hangs.
It does, but I am able to C-g
out of it, which isn't possible in the inferior Python shell.
Please also report the value of
python-mls-continuation-prompt
in your shell buffer.
Doing C-h v python-mls-continuation-prompt
when inside the inferior Python buffer gives me this:
python-mls-continuation-prompt is a variable defined in ‘python-mls.el’.
Its value is #("... " 0 4 (font-lock-face comint-highlight-prompt))
Documentation:
Current computed continuation prompt.
Ok that’s useful. Maybe the windows process isn’t responding to the interrupt. Try just python-mls-interrupt
, perhaps after a long sleep command. If that doesn’t work, try just C-c C-c
In the same situation.
Actually, once Emacs hangs, there's nothing I can do anymore... C-g
doesn't do anything, nor does C-c C-c
, even after waiting a bit. (IME, comint buffers sometimes need a little time to process C-c C-c
, but it this case, it never happens.) Emacs is completely stuck, I can't even click the close button in the window bar. I need to open the task manager to kill the process. (The process actually runs at about 30% CPU and makes the fans of my laptop spin up...)
I tried setting a timer in an IELM buffer with (run-with-timer 10 nil #'python-mls-interrupt)
, but even if that's the correct way of doing it, it didn't have any effect either...
OK, what about just C-c C-c
on a time.sleep(10)
in a fresh session? You can even disable python-mls for this test. You should get something like:
Python 3.10.0 (default, Oct 13 2021, 06:45:00) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> python.el: native completion setup loaded
>>> import time
>>> time.sleep(10)
C-c C-cTraceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyboardInterrupt
>>>
Yup, that's exactly what I get (except for the native completion message):
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import time
>>> time.sleep(10)
C-c C-cTraceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyboardInterrupt
>>>
OK, it's good news that interruption is working. Let's see if things are being sent correctly through the pre-output filter which hides things behind the scenes:
(defun my/python-mls-preout-log (string)
(let ((buffer
(get-buffer-create "*PMLSPO*")))
(display-buffer buffer)
(with-current-buffer buffer
(insert "\nGOT:\n" string))))
(advice-add #'python-shell-output-filter :before #'my/python-mls-preout-log)
And let me know what you see in the *PMLSPO*
buffer that appears, when you try a continued command like if True: [Ret]
. BTW, you can interrupt a stuck Emacs by sending it a SIGUSR2 signal from the outside.
OK, it's good news that interruption is working. Let's see if things are being sent correctly through the pre-output filter which hides things behind the scenes:
The *PMLSPO*
buffer shows this a few times:
GOT:
>>>
But those are all added before I hit RET. Once I hit RET after e.g., if True:
, nothing is added anymore.
BTW, you can interrupt a stuck Emacs by sending it a SIGUSR2 signal from the outside.
That doesn't seem to work for Windows processes, unfortunately.
BTW, I usually run Python in a virtual env, but just now I accidentally did M-x run-python
without activating a venv first (which I do in Emacs with pyvenv-activate
) and this warning popped up:
Warning (python): Python shell prompts cannot be detected.
If your emacs session hangs when starting python shells
recover with ‘keyboard-quit’ and then try fixing the
interactive flag for your interpreter by adjusting the
‘python-shell-interpreter-interactive-arg’ or add regexps
matching shell prompts in the directory-local friendly vars:
+ ‘python-shell-prompt-regexp’
+ ‘python-shell-prompt-block-regexp’
+ ‘python-shell-prompt-output-regexp’
Or alternatively in:
+ ‘python-shell-prompt-input-regexps’
+ ‘python-shell-prompt-output-regexps’
This doesn't happen when I first activate a venv and then do M-x run-python
, and the problem with python-mls
occurs either way, so it's probably not related, but I since it seems to involve the prompt, I thought I'd mention it. (The functionality of the Python shell doesn't appear to be affected by this warning, so I'm not sure what the warning is about. And if that makes me sound like a total noob, then I guess when it comes to Python shells, that's probably true. On Linux, this stuff just seems to work... 😲 )
OK, this doesn't look right. It should mention the interrupt. Something like:
GOT:
KeyboardInterrupt
GOT:
>>>
GOT:
KeyboardInterrupt
GOT:
>>>
To debug further, please deactivate again the advice for check-prompt as in #7. Then try if True: [Ret]
. You should hopefully see ...
. Does C-c C-c
work to return the prompt again in this situation? And with the above log advice still active, also try M-: (python-mls-interrupt-quietly)
again and report the new content in the log buffer.
Python shell prompts cannot be detected
You just need python-shell-interpreter-args
set to "-i"
I bet.
OK, this doesn't look right. It should mention the interrupt. Something like:
GOT: KeyboardInterrupt
Nope, nothing like that.
To debug further, please deactivate again the advice for check-prompt as in #7. Then try
if True: [Ret]
. You should hopefully see...
. DoesC-c C-c
work to return the prompt again in this situation?
Yes, the ...
is visible. Emacs is indeed unresponsive but now I can C-c C-c
out of it:
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> if True:
... C-c C-c
Traceback (most recent call last):
File "C:\Users\joost.kremers\.pyenv\pyenv-win\versions\3.9.6\lib\encodings\cp1252.py", line 14, in decode
def decode(self,input,errors='strict'):
KeyboardInterrupt
The above exception was the direct cause of the following exception:
KeyboardInterrupt: decoding with 'cp1252' codec failed (KeyboardInterrupt: )
>>> >>>
And with the above log advice still active, also try
M-: (python-mls-interrupt-quietly)
again and report the new content in the log buffer.
Nothing's added to the log buffer if I do that. Here's what I did:
my/python-mls-preout-log
and advised python-shell-output-filter
with it, as per above..py
file in the venv's project.M-: (python-mls-interrupt-quietly)
At this point, Emacs is stuck, but I can get back control with C-g
. No *PMLSPO*
buffer appears.
If I then type something at the prompt, the buffer pops up with:
GOT:
Traceback (most recent call last):
File "C:\Users\joost.kremers\.pyenv\pyenv-win\versions\3.9.6\lib\encodings\cp1252.py", line 14, in decode
def decode(self,input,errors='strict'):
KeyboardInterrupt
The above exception was the direct cause of the following exception:
KeyboardInterrupt: decoding with 'cp1252' codec failed (KeyboardInterrupt: )
>>>
GOT:
>>>
GOT:
>>>
GOT:
The error message wasn't there before, BTW...
I tried M-: (python-mls-interrupt-quietly)
again, but with the same result (Emacs gets stuck, I need to type C-g
and nothing is added to the log buffer.)
Your nested exception and double prompt could be the issue, which isn't really in the domain of python-mls. With check-prompt disabled, on C-c C-c
at the ...
, I get a simple & clean:
>>> if True:
... C-c C-c
KeyboardInterrupt
>>>
Please try the first exercise again, after doing M-x trace-function python-shell-comint-end-of-output-p
, and report the trace output.
Also, separately, can you try the above, but skipping these steps:
Just start with M-x run-python
.
Your nested exception and double prompt could be the issue, which isn't really in the domain of python-mls. With check-prompt disabled, on
C-c C-c
at the...
, I get a simple & clean:>>> if True: ... C-c C-c KeyboardInterrupt >>>
Tried this again, and it turns out Emacs doesn't become unresponsive at all. For some reason, TAB did not indent, but I can type four spaces and continue inputting code.
Still, doing C-c C-c
gives me the exception I pasted above.
Please try the first exercise again,
The first exercise being to disable the advice for comint-output-filter
and then tying if True: [RET]
in the Python REPL?
after doing
M-x trace-function python-shell-comint-end-of-output-p
, and report the trace output.
I get:
======================================================================
1 -> (python-shell-comint-end-of-output-p "... ")
1 <- python-shell-comint-end-of-output-p: 0
======================================================================
1 -> (python-shell-comint-end-of-output-p "Traceback (most recent call last):
File \"C:\\Users\\joost.kremers\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\encodings\\cp1252.py\", line 14, in decode
def decode(self,input,errors='strict'):
KeyboardInterrupt
The above exception was the direct cause of the following exception:
KeyboardInterrupt: decoding with 'cp1252' codec failed (KeyboardInterrupt: )
>>> ")
1 <- python-shell-comint-end-of-output-p: 352
Did you disable the advice to comint-output-filter and C-c C-c? I meant do NOT disable the advice, try an if True:
as normal, but with this trace-function. This is the type of thing that would be a 5min debug if I could reproduce.
Emacs doesn't become unresponsive at all
That's expected when you have disabled the advice. To make this clearer, python-mls:
Your Emacs is hanging at step 2. It is awaiting process output that never arrives. Our job is to figure out why.
Did you disable the advice to comint-output-filter and C-c C-c? I meant do NOT disable the advice, try an
if True:
as normal, but with this trace-function.
Ok, I'll have to post a screen shot, because once I type if True:
, Emacs hangs:
This is the type of thing that would be a 5min debug if I could reproduce.
Yeah, I'm sorry about all the trouble this is causing... I do have a Windows partition lying around on another laptop that I hardly ever use. I could put Emacs and Python on it and give you remote access, if that would help...
Unfortunately that doesn't include the output from the interruption itself (no ...
in the output). If you want to give me remote access I could take a look. Before that though, do try with a normal M-x run-python
and no fancy venv. And also perhaps enable both the logging and tracing and repeat the hang to see if that gives any insight.
Unfortunately that doesn't include the output from the interruption itself (no
...
in the output).
No, but that's because once I hit [RET]
after if True:
, Emacs hangs. I did try C-c C-c
, but there's simply no response.
If you want to give me remote access I could take a look.
I've set up Python and Emacs there with minimal configuration. So if you want to take a look, send me an e-mail. (Address is in my Github profile.)
Before that though, do try with a normal
M-x run-python
and no fancy venv. And also perhaps enable both the logging and tracing and repeat the hang to see if that gives any insight.
Let me include another screen shot. Here's what I did:
python-mls
, defines my/python-mls-preout-log
and adds it as advice to python-shell-output-filter
.M-x run-python
(so no venv).M-x trace-function python-shell-comint-end-of-output-p
print(True) [RET]
.if True:
but do not hit [RET]
yet.*PMLSPO*
and the *trace-output*
buffers are both visible.[RET]
. ==> No output is added to either buffer.C-c C-c
, but Emacs hangs. ==> No output is added to either buffer.Thanks for the very careful description. I’ll email.
FYI, I did email yesterday (in case it landed in your spam box).
@joostkremers let me know how you'd like to proceed.
It seems the key here is python-mls-interrupt-quietly
, which is called when the ...
continuation prompt is found in the output. To see if Emacs is stuck waiting for process output that never comes, you could see whether the following small change allows you to recover from if True: [Ret]
.
Alter this:
(while python-shell-output-filter-in-progress
(accept-process-output))
to add a 2.5s timeout:
(while python-shell-output-filter-in-progress
(accept-process-output nil 2.5))
This isn't a fix, just zeroing in on the issue.
No, I'm still not able to recover from if True: [RET]
, but I do notice that Emacs is no longer consuming 30% of CPU. Instead it's at 0%. I killed and restarted Emacs a few times, so it's consistent, and when I remove the timeout, CPU goes back to 30%.
Wow, it sounds well and truly hung. So perhaps it is just looping over and over receiving no output, with the delay giving it some CPU breathing room.
We can also try limiting the accept loop iterations (again, not a fix):
(let ((cnt 1))
(while (and (< cnt 10) python-shell-output-filter-in-progress)
(accept-process-output nil 1)
(cl-incf cnt)))
Yup, that gives me the prompt after waiting about 10 secs. After that, I can continue the if
statement normally and execute it, and python-mls
then works as it's supposed to:
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> if True:
print (1)
1
>>> if True:
print (2)
2
>>> if True:
print(3)
3
>>> if True:
print(4)
4
>>> if True:
print(4)
print(5)
else:
print(6)
4
5
>>>
Very interesting! I presume the (decorative only) leading ...
was shown as well, even in the 1st stuck case? So it seems there is an issue with the first interruption (only) producing no output, or perhaps the first interruption's output is getting missed somehow. Of course python-mls is trying to be very careful to wait for that output (a Keyboard Interrupt and a prompt) before allowing you to send another command. So this is the issue.
Can you try your if True: [Ret] C-c
in a bare newly run python process in the Windows shell (same interpreter as Emacs would run) and report on what it does? Then try another if True:
in the same process. Any difference in output?
Very interesting! I presume the (decorative only) leading
...
was shown as well,
Right! I didn't even notice it was absent from the output I posted. Yes, the ...
is shown on every indented line.
even in the 1st stuck case?
You mean, after I type if True: [RET]
and the loop times out? Yes.
Can you try your
if True: [Ret] C-c
in a bare newly run python process in the Windows shell (same interpreter as Emacs would run) and report on what it does? Then try anotherif True:
in the same process. Any difference in output?
No, no difference.
To make sure we understand each other correctly, though: if True: [RET]
hangs Emacs each time if I type it directly. What I meant when I said that "python-mls
works as it's supposed to" was that I can hit up arrow and edit the previous input. But if I type if True: [RET]
again, Emacs hangs again.
I didn't even notice it was absent from the output I posted.
In fact that's a feature (so the decorative ...
's are removed and reinserted on copy/paste from anywhere).
if True: [RET] hangs Emacs each time if I type it directly.
OK. I had understood you to mean that you could compose multi-line commands as expected after the first times out. I suppose you can also try C-j
from the first line instead of [Ret]
; that should also work normally.
No, no difference.
And what does that output look like after you C-c
a continuing command? BTW, if you can try re-enabling the tracing and logging code we used earlier, and leaving the timeout/loop max count intact, entering a dreaded if True: [Ret]
, we might in fact get some output to examine after the while loop ends. If you'd prefer to go the remote debug route instead of the back and forth, happy to give that a try.
With the logging and the tracing active, the moment the timeout ends, the *trace-output*
buffer shows the following:
1 -> (python-shell-comint-end-of-output-p "... ")
1 <- python-shell-comint-end-of-output-p: 0
======================================================================
The *PMLSPO*
buffer shows nothing.
When I then start typing, after the third character, the keyboard interrupt happens. The *trace-output*
buffer shows this:
1 -> (python-shell-comint-end-of-output-p "Traceback (most recent call last):
File \"C:\\Users\\joost.kremers\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\encodings\\cp1252.py\", line 14, in decode
")
1 <- python-shell-comint-end-of-output-p: nil
======================================================================
1 -> (python-shell-comint-end-of-output-p "Traceback (most recent call last):
File \"C:\\Users\\joost.kremers\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\encodings\\cp1252.py\", line 14, in decode
def decode(self,input,errors='strict'):
KeyboardInterrupt
The above exception was the direct cause of the following exception:
KeyboardInterrupt: decoding with 'cp1252' codec failed (KeyboardInterrupt: )
>>> ")
1 <- python-shell-comint-end-of-output-p: 352
======================================================================
And the *PMLSPO*
buffer this:
GOT:
Traceback (most recent call last):
File "C:\Users\joost.kremers\.pyenv\pyenv-win\versions\3.9.6\lib\encodings\cp1252.py", line 14, in decode
GOT:
def decode(self,input,errors='strict'):
KeyboardInterrupt
The above exception was the direct cause of the following exception:
KeyboardInterrupt: decoding with 'cp1252' codec failed (KeyboardInterrupt: )
>>>
GOT:
>>>
GOT:
>>>
Not sure if that helps in any way...
OK I got access to a Windows machine with Emacs 27.2 and python v3.10, and could reproduce this. Here is the issue: interrupt-process
(an internal C function) does not function correctly to interrupt Python on Windows emacs! While at the command prompt, a simple C-c
does correctly interrupt (and returns Keyboard Interrupt\n>>>
promptly), interrupt-process
does not, and leads to the double-traceback you reported earlier, but only after you send another newline. This is incorrect behavior, and seems to represent a bug in the way Emacs sends interrupts on Windows, which differs from C-c
in the Windows command prompt.
It's easy to verify the difference:
C-c
at >>>
python prompt in "Command Prompt": immediate KeyboardInterrupt
and prompt.C-c C-c
at >>>
python prompt in Emacs Python: no KeyboardInterrupt
.So python-mls
is waiting "forever" for a prompt which will never show up. This needs to be fixed upstream, so I'd strongly encourage you to report an emacs bug (and maybe test on Emacs 28 first).
In the meantime, as a cheap workaround, you can add:
(process-send-string nil "\n")
to the end of python-mls-interrupt
to send a newline and just swallow all the errors. A longer term solution requires fixing the incorrect behavior of Emacs->Python interruption.
Thanks for diving into this! Glad you found another Windows machine to debug on... 😄
So
python-mls
is waiting "forever" for a prompt which will never show up. This needs to be fixed upstream, so I'd strongly encourage you to report an emacs bug (and maybe test on Emacs 28 first).
Will do.
In the meantime, as a cheap workaround, you can add:
(process-send-string nil "\n")
to the end of
python-mls-interrupt
to send a newline and just swallow all the errors.
Thanks, that works.
A longer term solution requires fixing the incorrect behavior of Emacs->Python interruption.
Which, depending on the nature of the bug, may not happen until Emacs 29, 28 being in pretest now...
Thanks for your help looking into this. Variation in process handling are a common issue I think.
Emacs on MS-Windows communicates with async sub-processes via pipes, not via PTYs. Does Python on MS-Windows react to the C-c character received from a pipe as it reacts to it when it's attached to a console device? If not, this is the root cause of your problem (although I admit I don't really understand the significance of all the talk about backtraces, and I have no idea what does python-mls
do, so I might be missing something).
FTR, Emacs on Windows implements sending SIGINT to a sub-process by setting the foreground window to that of the sub-process, and then simulating the C-c keystroke. IOW, it injects a Ctrl-C character into the input stream of the sub-process. What happens as the result is entirely up to the sub-process, but I could understand why a sub-process whose stdin is a pipe would not react to Ctrl-C as it does when it runs interactively.
Thanks, this is helpful. The simple idea is to notice Python is in the middle of a continued statement (which it indicates with a ...
prompt), interrupt that, and recompose using Emacs capabilities. On other systems aside from Windows, interrupt-process
immediately returns to the prompt. When running python
on Windows from the "Command Prompt", C-c
also leads to the correct behavior. But when run as an emacs sub-process, python complains about "decoding errors" (decoding with 'cp1252' codec failed
), and does not return to the prompt. I'm not a Windows user, but I wonder if this problem is unique to Python.
I'd speculate, based on this answer, that Python attempts to decode the C-c
character over the pipe using the Windows-specific ANSI-like cp1252 codec; not sure why this C-c from Emacs isn't being encoded correctly to 0x03
. Perhaps one of the C1
control characters that window-1252 does not define is being sent as well.
I wonder if this problem is unique to Python.
It isn't.
I'd speculate, based on this answer, that Python attempts to decode the C-c character over the pipe
It could indeed be a side effect of the attempt to decide. At this point, I'd suggest to ask the Python developers, they might know the real answer. Specifically, I'd ask them whether Python is expected to behave like it got SIGINT when it gets Ctrl-C but its stdin is a pipe.
At this point, I'd suggest to ask the Python developers
It could be that Python closes its window when its stdin is a pipe, in which case the method used by Emacs to deliver SIGINT to it will simply not work on MS-Windows.
Thanks, Eli. @joostkremers would you be willing to put this issue to the python devs? I'm not a Windows users so couldn't offer them much in the way of testing.
@joostkremers would you be willing to put this issue to the python devs? I'm not a Windows users so couldn't offer them much in the way of testing.
Yeah, sure. Though I suspect some of the devs will have access to a Windows machine to test on. :smile:
When I type an expression into an inferior Python buffer that expects an indented code block, Emacs hangs the moment I hit RET.
C-g
orESC ESC ESC
doesn't get me out of it, I need to kill the process through the task manager.This is in Emacs 27.2 on Windows. On Linux, with mostly the same setup, everything works.
At this point, the moment I type
RET
, point jumps to the next line and then Emacs hangs. The continuation prompt (...
) does not appear.