Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5cd5067

Browse filesBrowse files
committed
Handle ctrl+c and ctrl+d (for exiting) better.
1 parent ec0230c commit 5cd5067
Copy full SHA for 5cd5067

1 file changed

+47-1Lines changed: 47 additions & 1 deletion

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎bpython/urwid.py‎

Copy file name to clipboardExpand all lines: bpython/urwid.py
+47-1Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import sys
3939
import os
4040
import locale
41+
import signal
4142
from types import ModuleType
4243
from optparse import Option
4344

@@ -309,17 +310,34 @@ def reprint_line(self, lineno, tokens):
309310
edit.set_edit_markup(list(format_tokens(tokens)))
310311

311312
def push(self, s, insert_into_history=True):
313+
# Restore the original SIGINT handler. This is needed to be able
314+
# to break out of infinite loops. If the interpreter itself
315+
# sees this it prints 'KeyboardInterrupt' and returns (good).
316+
orig_handler = signal.getsignal(signal.SIGINT)
317+
signal.signal(signal.SIGINT, signal.default_int_handler)
312318
# Pretty blindly adapted from bpython.cli
313319
try:
314320
return repl.Repl.push(self, s, insert_into_history)
315321
except SystemExit:
316322
raise urwid.ExitMainLoop()
323+
except KeyboardInterrupt:
324+
# KeyboardInterrupt happened between the except block around
325+
# user code execution and this code. This should be rare,
326+
# but make sure to not kill bpython here, so leaning on
327+
# ctrl+c to kill buggy code running inside bpython is safe.
328+
self.keyboard_interrupt()
329+
finally:
330+
signal.signal(signal.SIGINT, orig_handler)
317331

318332
def start(self):
319333
# Stolen from bpython.cli again
320334
self.push('from bpython._internal import _help as help\n', False)
321335
self.prompt(False)
322336

337+
def keyboard_interrupt(self):
338+
# Do we need to do more here? Break out of multiline input perhaps?
339+
self.echo('KeyboardInterrupt')
340+
323341
def prompt(self, more):
324342
# XXX what is s_hist?
325343
if not more:
@@ -382,8 +400,16 @@ def handle_input(self, event):
382400
# XXX what is this s_hist thing?
383401
self.stdout_hist += inp + '\n'
384402
self.edit = None
403+
# This may take a while, so force a redraw first:
404+
self.main_loop.draw_screen()
385405
more = self.push(inp)
386406
self.prompt(more)
407+
elif event == 'ctrl d':
408+
# ctrl+d on an empty line exits
409+
if self.edit is not None and not self.edit.get_edit_text():
410+
raise urwid.ExitMainLoop()
411+
#else:
412+
# self.echo(repr(event))
387413

388414

389415
def main(args=None, locals_=None, banner=None):
@@ -461,6 +487,13 @@ def main(args=None, locals_=None, banner=None):
461487
myrepl = URWIDRepl(loop, frame, listbox, overlay, tooltip,
462488
interpreter, statusbar, config)
463489

490+
if options.reactor:
491+
# Twisted sets a sigInt handler that stops the reactor unless
492+
# it sees a different custom signal handler.
493+
def sigint(*args):
494+
reactor.callFromThread(myrepl.keyboard_interrupt)
495+
signal.signal(signal.SIGINT, sigint)
496+
464497
# XXX HACK: circular dependency between the event loop and repl.
465498
# Fix by not using unhandled_input?
466499
loop._unhandled_input = myrepl.handle_input
@@ -515,7 +548,20 @@ def run_find_coroutine():
515548

516549
loop.set_alarm_in(0, start)
517550

518-
loop.run()
551+
while True:
552+
try:
553+
loop.run()
554+
except KeyboardInterrupt:
555+
# HACK: if we run under a twisted mainloop this should
556+
# never happen: we have a SIGINT handler set.
557+
# If we use the urwid select-based loop we just restart
558+
# that loop if interrupted, instead of trying to cook
559+
# up an equivalent to reactor.callFromThread (which
560+
# is what our Twisted sigint handler does)
561+
loop.set_alarm_in(0,
562+
lambda *args: myrepl.keyboard_interrupt())
563+
continue
564+
break
519565

520566
if config.hist_length:
521567
histfilename = os.path.expanduser(config.hist_file)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.