-
-
Notifications
You must be signed in to change notification settings - Fork 645
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
Ctrl+C has unusual behaviour while Python waits on input()
#1130
Comments
I found out how to change this behaviour in A.P.P. Since I am fairly intimidated by the codebase and not sure what to do, I thought I'd document the process I took in this comment to help other newcomers build up enough confidence and learn the skills to make their own changes. Click here to read the processHere's the thought process I went through:Since the A.P.P. source tree is different to the Cosmos Python source tree, they probably have different code for Ctrl+C handling. My findings for one build will probably not apply to the other build, so I should pick one build to investigate and stick to it. I'll pick A.P.P. so that I only have to investigate code that's inside the monorepo. This means I can search the entire codebase at once with ag, and if ag shows no results, it's not there. Running A.P.P with --strace showed me that just before the call Well, regardless, since the default Upon seeing the big list of changes at the top of Cosmopolitan's linenoise, it's clear that it's been carefully designed to have a lot of features. I start to think that the doubled Ctrl+C behaviour may be deliberately added as a feature. In the Shortcuts section at the top of the file, it says If I wanted to be really sure this behaviour isn't anything to do with Python, I could build a short C program that calls Cosmopolitan linenoise for input and I could try Ctrl+C there to see what happens. I decide not to do this because I'm not confident enough in my ability to write C, but the idea was still good. Now I want to figure out why and how linenoise does it so I can turn it off. I check the blame for that line and find a commit which changed the comment from single Ctrl+C to doubled Ctrl+C. Comments aren't code - let's see if this code also changed in this commit. This code was added: case CTRL('C'):
if (linenoiseRead(l.ifd, seq, sizeof(seq), &l) != 1) break;
switch (seq[0]) {
CASE(CTRL('C'), linenoiseEditInterrupt(&l));
CASE(CTRL('B'), linenoiseEditBarf(&l));
CASE(CTRL('S'), linenoiseEditSlurp(&l));
default:
break;
}
break; It looks like after Ctrl+C it reads a second time and if it sees a second Ctrl+C it calls linenoiseEditInterrupt. (And there's also some shortcuts on Ctrl+C Ctrl+B and Ctrl+C Ctrl+C called "barf" and "slurp" - a web search tells me these are clones of Emacs Paredit features.) In theory I can replace the // handle (1) emacs keyboard combos
// (2) otherwise sigint exit
if (seq[0] == CTRL('C')) {
DUFF_ROUTINE_READ(3);
if (rc == 1) {
switch (seq[0]) {
CASE(CTRL('C'), linenoiseEditInterrupt(l));
CASE(CTRL('B'), linenoiseEditBarf(l));
CASE(CTRL('S'), linenoiseEditSlurp(l));
default:
goto HandleRead;
}
continue;
} else {
goto HandleRead;
}
} OK, let's try changing the code. diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c
index cf673ba1f..bcd5080fe 100644
--- a/third_party/linenoise/linenoise.c
+++ b/third_party/linenoise/linenoise.c
@@ -2044,19 +2044,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
// handle (1) emacs keyboard combos
// (2) otherwise sigint exit
if (seq[0] == CTRL('C')) {
- DUFF_ROUTINE_READ(3);
- if (rc == 1) {
- switch (seq[0]) {
- CASE(CTRL('C'), linenoiseEditInterrupt(l));
- CASE(CTRL('B'), linenoiseEditBarf(l));
- CASE(CTRL('S'), linenoiseEditSlurp(l));
- default:
- goto HandleRead;
- }
- continue;
- } else {
- goto HandleRead;
- }
+ linenoiseEditInterrupt(l);
}
// handle (1) unpausing terminal after ctrl-s Now I have to rebuild Python. The wiki tells me how - Conclusion: In A.P.P., this behaviour is deliberate because Python's input reader has been replaced with linenoise, and linenoise has special code that maps Ctrl+C Ctrl+C = SIGINT. Changing the shortcut in linenoise to single Ctrl+C = SIGINT makes it work as I expected. I'm tempted to open a pull request for this keyboard shortcut change, but I also removed the barf and slurp shortcuts and left them as dead code, so first I'll need to find some new key chords to put those shortcuts on. Next I will investigate the Cosmos Python behaviour, since as far as I can tell from the source tree, it doesn't have linenoise so the fix might be a bit different. |
For Cosmos Python it turns out I think it would be good to have Cosmos Python |
In Cosmos Python, Ctrl+C doesn't work as expected because Cosmopolitan Libc #include <stdio.h>
#include <signal.h>
static void handler(int signo) {
fprintf(stderr, "SIGINT");
fflush(stderr);
}
int main(void) {
// Call `handler` when SIGINT received (when Ctrl+C pressed)
struct sigaction action;
action.sa_handler = handler;
sigaction(SIGINT, &action, NULL);
// Get some input with `fgets`
char buf[20];
printf("hello! ");
fflush(stdout);
fgets(buf, 20, stdin);
// End program after getting input
return 0;
} > gcc ctrl-c.c -o ctrl-c
> ./ctrl-c
hello! ^CSIGINT <-- program ends straight away
> cosmocc ctrl-c.c -o ctrl-c
> ./ctrl-c
hello! ^CSIGINT^CSIGINT^CSIGINT^CSIGINT <-- program doesn't end Cosmopolitan's behaviour is easy to visualise with
I found out that this happens because in I edited Cosmopolitan to change cosmopolitan/libc/stdio/fgets_unlocked.c Lines 58 to 60 in b9d6e6e
and rebuilt Cosmos Python. Ctrl+C in Python input() in my new build now works like I expected.
I don't know what the C standard says |
After I reading about In my previous comment, I wrote:
Turns out this is what the I'm pretty sure this Cosmopolitan Libc |
See investigation in jart#1130. fgets internally calls readv. readv is a @restartable function that understands SA_RESTART. If SA_RESTART is set, readv already handles restarting the system call and eventually the string is transparently returned to the fgets caller. When SA_RESTART is not set, -1 EINTR should bubble up to the fgets caller. However, until this commit, fgets itself would detect EINTR and keep retrying until it read an entire line. This commit fixes this behaviour so that fgets understands SA_RESTART. I hereby waive copyright to this commit and place it in public domain. Signed-off-by: Cadence Ember <cadence@disroot.org>
See investigation in jart#1130. fgets internally calls readv. readv is a @restartable function that understands SA_RESTART. If SA_RESTART is set, readv already handles restarting the system call and eventually the string is transparently returned to the fgets caller. When SA_RESTART is not set, -1 EINTR should bubble up to the fgets caller. However, until this commit, fgets itself would detect EINTR and keep retrying until it read an entire line. This commit fixes this behaviour so that fgets understands SA_RESTART. I hereby assign copyright for this commit to Justine Tunney. Signed-off-by: Cadence Ember <cadence@disroot.org>
See investigation in jart#1130. fgets internally calls readv. readv is a @restartable function that understands SA_RESTART. If SA_RESTART is set, readv already handles restarting the system call and eventually the string is transparently returned to the fgets caller. When SA_RESTART is not set, -1 EINTR should bubble up to the fgets caller. However, until this commit, fgets itself would detect EINTR and keep retrying until it read an entire line. This commit fixes this behaviour so that fgets understands SA_RESTART. I hereby assign copyright for this commit to Justine Tunney. Signed-off-by: Cadence Ember <cadence@disroot.org>
I ran this file in these builds of Python:
Expected behaviour
make o//third_party/python/python.com
Press twice: Traceback
https://cosmo.zip/pub/cosmos/bin/python
Press repeatedly: No effect
Press Ctrl+C and Enter: Print ^C and traceback
I tested on Linux and macOS and it seems to be the same in both.
I would expect Ctrl+C to traceback straight away regardless of whether the program is running or waiting for input. Is it possible to change this behaviour in Cosmopolitan Python?
The text was updated successfully, but these errors were encountered: