@@ -109,6 +109,7 @@ def repl():
109109 got_sigint = 0
110110 statement = ''
111111 readline_skip_chars = 0
112+ inner_loop = False
112113
113114 def save_history ():
114115 nonlocal histfile
@@ -126,20 +127,43 @@ def repl():
126127 def sigint_handler (signum , frame ):
127128 """
128129 Handle ^C by aborting the entry of the current statement and quitting when double-struck.
130+
131+ Sometimes this happens in the main input() function. When that happens statement is "", because
132+ we have not yet returned from input(). Sometimes it happens in the middle of the inner loop's
133+ input() - in that case, statement is the beginning of a multiline expression. Hitting ^C in the
134+ middle of a multiline express cancels its input, but readline's input() doesn't return, so we
135+ have to print the extra > prompt and fake it by later getting rid of the first readline_skip_chars
136+ characters from the input buffer.
129137 """
130138 nonlocal got_sigint
131139 nonlocal statement
132140 nonlocal readline_skip_chars
141+ nonlocal inner_loop
133142
134143 got_sigint = got_sigint + 1
135- if (got_sigint == 1 and statement == "" and readline .get_line_buffer () == "" ):
136- sys .stdout .write ("\n (To exit, press Ctrl+C again or Ctrl+D or type .exit)" )
137- elif (got_sigint > 1 ):
144+ if (got_sigint > 1 ):
138145 sys .stdout .write ("\n " )
139146 quit ()
140147
148+ if (inner_loop != True ):
149+ if (got_sigint == 1 and len (readline .get_line_buffer ()) == readline_skip_chars ):
150+ # First ^C with nothing in the input buffer
151+ sys .stdout .write ("\n (To exit, press Ctrl+C again or Ctrl+D or type .exit)" )
152+ elif (got_sigint == 1 and readline .get_line_buffer () != "" ):
153+ # Input buffer has text - clear it
154+ got_sigint = 0
155+ readline_skip_chars = len (readline .get_line_buffer ())
156+ else :
157+ if (got_sigint == 1 and statement == "" and len (readline .get_line_buffer ()) == readline_skip_chars ):
158+ # statement == "" means that the inner loop has already seen ^C and is now faking the outer loop
159+ sys .stdout .write ("\n (To exit, press Ctrl+C again or Ctrl+D or type .exit)" )
160+ elif (got_sigint == 1 and statement != "" ):
161+ # ^C happened on inner loop while it was still thinking we were doing a multiline-expression; since
162+ # we can't break the input() function, we set it up to return an outer expression and fake the outer loop
163+ got_sigint = 0
164+ readline_skip_chars = len (readline .get_line_buffer ())
165+
141166 sys .stdout .write ("\n > " )
142- readline_skip_chars = len (readline .get_line_buffer ())
143167 statement = ""
144168 signal .signal (signal .SIGINT , sigint_handler )
145169
@@ -156,6 +180,7 @@ def repl():
156180 #
157181 while got_sigint < 2 :
158182 try :
183+ inner_loop = False
159184 if (statement == "" ):
160185 statement = input ('> ' )[readline_skip_chars :]
161186 readline_skip_chars = 0
@@ -177,10 +202,11 @@ def repl():
177202 # SIGINT is received, so we have to patch things up so that the next-entered line is
178203 # treated as the input at the top of the loop.
179204 while (got_sigint == 0 ):
180- more = input ('... ' )[readline_skip_chars :]
205+ inner_loop = True
206+ lineBuffer = input ('... ' )
207+ more = lineBuffer [readline_skip_chars :]
181208 readline_skip_chars = 0
182209 if (got_sigint > 0 ):
183- got_sigint = 0
184210 statement = more
185211 break
186212 statement = statement + '\n ' + more
0 commit comments