@@ -155,7 +155,8 @@ def repl():
155155 got_sigint = 0
156156 statement = ''
157157 readline_skip_chars = 0
158-
158+ inner_loop = False
159+
159160 def save_history ():
160161 nonlocal histfile
161162 readline .write_history_file (histfile )
@@ -172,20 +173,43 @@ def repl():
172173 def sigint_handler (signum , frame ):
173174 """
174175 Handle ^C by aborting the entry of the current statement and quitting when double-struck.
176+
177+ Sometimes this happens in the main input() function. When that happens statement is "", because
178+ we have not yet returned from input(). Sometimes it happens in the middle of the inner loop's
179+ input() - in that case, statement is the beginning of a multiline expression. Hitting ^C in the
180+ middle of a multiline express cancels its input, but readline's input() doesn't return, so we
181+ have to print the extra > prompt and fake it by later getting rid of the first readline_skip_chars
182+ characters from the input buffer.
175183 """
176184 nonlocal got_sigint
177185 nonlocal statement
178186 nonlocal readline_skip_chars
187+ nonlocal inner_loop
179188
180189 got_sigint = got_sigint + 1
181- if (got_sigint == 1 and statement == "" and readline .get_line_buffer () == "" ):
182- sys .stdout .write ("\n (To exit, press Ctrl+C again or Ctrl+D or type .exit)" )
183- elif (got_sigint > 1 ):
190+ if (got_sigint > 1 ):
184191 sys .stdout .write ("\n " )
185192 quit ()
186193
194+ if (inner_loop != True ):
195+ if (got_sigint == 1 and len (readline .get_line_buffer ()) == readline_skip_chars ):
196+ # First ^C with nothing in the input buffer
197+ sys .stdout .write ("\n (To exit, press Ctrl+C again or Ctrl+D or type .exit)" )
198+ elif (got_sigint == 1 and readline .get_line_buffer () != "" ):
199+ # Input buffer has text - clear it
200+ got_sigint = 0
201+ readline_skip_chars = len (readline .get_line_buffer ())
202+ else :
203+ if (got_sigint == 1 and statement == "" and len (readline .get_line_buffer ()) == readline_skip_chars ):
204+ # statement == "" means that the inner loop has already seen ^C and is now faking the outer loop
205+ sys .stdout .write ("\n (To exit, press Ctrl+C again or Ctrl+D or type .exit)" )
206+ elif (got_sigint == 1 and statement != "" ):
207+ # ^C happened on inner loop while it was still thinking we were doing a multiline-expression; since
208+ # we can't break the input() function, we set it up to return an outer expression and fake the outer loop
209+ got_sigint = 0
210+ readline_skip_chars = len (readline .get_line_buffer ())
211+
187212 sys .stdout .write ("\n > " )
188- readline_skip_chars = len (readline .get_line_buffer ())
189213 statement = ""
190214 signal .signal (signal .SIGINT , sigint_handler )
191215
@@ -202,6 +226,7 @@ def repl():
202226 #
203227 while got_sigint < 2 :
204228 try :
229+ inner_loop = False
205230 if (statement == "" ):
206231 statement = input ('> ' )[readline_skip_chars :]
207232 readline_skip_chars = 0
@@ -223,10 +248,11 @@ def repl():
223248 # SIGINT is received, so we have to patch things up so that the next-entered line is
224249 # treated as the input at the top of the loop.
225250 while (got_sigint == 0 ):
226- more = input ('... ' )[readline_skip_chars :]
251+ inner_loop = True
252+ lineBuffer = input ('... ' )
253+ more = lineBuffer [readline_skip_chars :]
227254 readline_skip_chars = 0
228255 if (got_sigint > 0 ):
229- got_sigint = 0
230256 statement = more
231257 break
232258 statement = statement + '\n ' + more
0 commit comments