diff -r db08f902bb3a Doc/c-api/veryhigh.rst --- a/Doc/c-api/veryhigh.rst Thu Jun 04 14:34:43 2015 -0500 +++ b/Doc/c-api/veryhigh.rst Fri Jun 05 07:28:09 2015 +0000 @@ -149,11 +149,20 @@ Can be set to point to a function with the prototype ``int func(void)``. The function will be called when Python's interpreter prompt is about to become idle and wait for user input - from the terminal. The return value is ignored. Overriding this + from the terminal. Overriding this hook can be used to integrate the interpreter's prompt with other - event loops, as done in the :file:`Modules/_tkinter.c` in the + event loops, as done in the :file:`Modules/_tkinter.c` file in the Python source code. + The function should normally wait until input becomes available, + and then return a non-negative value. If a signal occurs, the + function should stop waiting, set :c:data:`errno` to :const:`EINTR`, + and return a negative value. This gives the caller a chance to + execute Python signal handlers or interrupt the operation. + + .. versionchanged:: 3.6 + The return value is now significant. + .. c:var:: char* (*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) diff -r db08f902bb3a Modules/_tkinter.c --- a/Modules/_tkinter.c Thu Jun 04 14:34:43 2015 -0500 +++ b/Modules/_tkinter.c Fri Jun 05 07:28:09 2015 +0000 @@ -25,6 +25,7 @@ #include "Python.h" #include +#include #ifdef WITH_THREAD #include "pythread.h" @@ -3378,12 +3379,29 @@ static PyThreadState *event_tstate = NULL; #endif +static Tcl_AsyncHandler handler; + +static void sigint_handler(int signum) +{ + Tcl_AsyncMark(handler); +} + +static int +interrupt_handler(ClientData clientData, Tcl_Interp *interp, int code) +{ + int* flag = (int*)clientData; + *flag = 1; + return code; +} + static int EventHook(void) { #ifndef MS_WINDOWS int tfile; #endif + int interrupted = 0; + PyOS_sighandler_t py_sigint_handler; #ifdef WITH_THREAD PyEval_RestoreThread(event_tstate); #endif @@ -3393,6 +3411,8 @@ tfile = fileno(stdin); Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL); #endif + py_sigint_handler = PyOS_setsig(SIGINT, sigint_handler); + handler = Tcl_AsyncCreate(interrupt_handler, (ClientData)&interrupted); while (!errorInCmd && !stdin_ready) { int result; #ifdef MS_WINDOWS @@ -3416,10 +3436,13 @@ #else result = Tcl_DoOneEvent(0); #endif + if (interrupted) break; if (result < 0) break; } + PyOS_setsig(SIGINT, py_sigint_handler); + Tcl_AsyncDelete(handler); #ifndef MS_WINDOWS Tcl_DeleteFileHandler(tfile); #endif @@ -3432,6 +3455,11 @@ #ifdef WITH_THREAD PyEval_SaveThread(); #endif + if (interrupted) { + raise(SIGINT); + errno = EINTR; + return -1; + } return 0; } diff -r db08f902bb3a Modules/readline.c --- a/Modules/readline.c Thu Jun 04 14:34:43 2015 -0500 +++ b/Modules/readline.c Fri Jun 05 07:28:09 2015 +0000 @@ -1120,28 +1120,28 @@ completed_input_string = not_done_reading; while (completed_input_string == not_done_reading) { - int has_input = 0, err = 0; + int has_input = 0; - while (!has_input) - { struct timeval timeout = {0, 100000}; /* 0.1 seconds */ - + while (!has_input) { + struct timeval timeout = {0, 100000}; /* 0.1 seconds */ /* [Bug #1552726] Only limit the pause if an input hook has been defined. */ struct timeval *timeoutp = NULL; - if (PyOS_InputHook) + if (PyOS_InputHook) { timeoutp = &timeout; + has_input = PyOS_InputHook(); + if (has_input < 0) break; + } FD_SET(fileno(rl_instream), &selectset); /* select resets selectset if no input was available */ has_input = select(fileno(rl_instream) + 1, &selectset, NULL, NULL, timeoutp); - err = errno; - if(PyOS_InputHook) PyOS_InputHook(); } if (has_input > 0) { rl_callback_read_char(); } - else if (err == EINTR) { + else if (errno == EINTR) { int s; #ifdef WITH_THREAD PyEval_RestoreThread(_PyOS_ReadlineTState); diff -r db08f902bb3a Parser/myreadline.c --- a/Parser/myreadline.c Thu Jun 04 14:34:43 2015 -0500 +++ b/Parser/myreadline.c Fri Jun 05 07:28:09 2015 +0000 @@ -37,45 +37,50 @@ char *p; int err; while (1) { - if (PyOS_InputHook != NULL) - (void)(PyOS_InputHook)(); - errno = 0; - clearerr(fp); - if (_PyVerify_fd(fileno(fp))) - p = fgets(buf, len, fp); - else - p = NULL; - if (p != NULL) - return 0; /* No error */ - err = errno; + int has_input = 0; + if (PyOS_InputHook != NULL) { + has_input = PyOS_InputHook(); + err = errno; + } + if (has_input >= 0) { + errno = 0; + clearerr(fp); + if (_PyVerify_fd(fileno(fp))) + p = fgets(buf, len, fp); + else + p = NULL; + if (p != NULL) + return 0; /* No error */ + err = errno; #ifdef MS_WINDOWS - /* Ctrl-C anywhere on the line or Ctrl-Z if the only character - on a line will set ERROR_OPERATION_ABORTED. Under normal - circumstances Ctrl-C will also have caused the SIGINT handler - to fire which will have set the event object returned by - _PyOS_SigintEvent. This signal fires in another thread and - is not guaranteed to have occurred before this point in the - code. + /* Ctrl-C anywhere on the line or Ctrl-Z if the only character + on a line will set ERROR_OPERATION_ABORTED. Under normal + circumstances Ctrl-C will also have caused the SIGINT handler + to fire which will have set the event object returned by + _PyOS_SigintEvent. This signal fires in another thread and + is not guaranteed to have occurred before this point in the + code. - Therefore: check whether the event is set with a small timeout. - If it is, assume this is a Ctrl-C and reset the event. If it - isn't set assume that this is a Ctrl-Z on its own and drop - through to check for EOF. - */ - if (GetLastError()==ERROR_OPERATION_ABORTED) { - hInterruptEvent = _PyOS_SigintEvent(); - switch (WaitForSingleObjectEx(hInterruptEvent, 10, FALSE)) { - case WAIT_OBJECT_0: - ResetEvent(hInterruptEvent); - return 1; /* Interrupt */ - case WAIT_FAILED: - return -2; /* Error */ + Therefore: check whether the event is set with a small timeout. + If it is, assume this is a Ctrl-C and reset the event. If it + isn't set assume that this is a Ctrl-Z on its own and drop + through to check for EOF. + */ + if (GetLastError()==ERROR_OPERATION_ABORTED) { + hInterruptEvent = _PyOS_SigintEvent(); + switch (WaitForSingleObjectEx(hInterruptEvent, 10, FALSE)) { + case WAIT_OBJECT_0: + ResetEvent(hInterruptEvent); + return 1; /* Interrupt */ + case WAIT_FAILED: + return -2; /* Error */ + } } - } #endif /* MS_WINDOWS */ - if (feof(fp)) { - clearerr(fp); - return -1; /* EOF */ + if (feof(fp)) { + clearerr(fp); + return -1; /* EOF */ + } } #ifdef EINTR if (err == EINTR) {