Skip to content

Commit a264966

Browse files
committed
Reorder startup so we can set sys.stdin to None.
This fixes an ugly traceback if exit() (not sys.exit(), exit()) is called. exit() close sys.stdin if it can, and urwid's shutdown code tries to use it. This fixes it by using urwid's support for starting and stopping the screen before/after running its mainloop: we start the screen, redirect std*, run the mainloop, then restore sys.std* before stopping the screen. Now exit() does not have a real sys.stdin to close. This mainly moves code around: the important part of the diff is sys.stdin is now set to None.
1 parent 5cd5067 commit a264966

File tree

1 file changed

+73
-69
lines changed

1 file changed

+73
-69
lines changed

bpython/urwid.py

Lines changed: 73 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -502,75 +502,79 @@ def sigint(*args):
502502
orig_stdin = sys.stdin
503503
orig_stdout = sys.stdout
504504
orig_stderr = sys.stderr
505-
try:
506-
# XXX aargh, we have to leave sys.stdin alone for now:
507-
# urwid.display_common.RealTerminal.tty_signal_keys calls
508-
# sys.stdin.fileno() instead of getting stdin passed in as
509-
# raw_display.Screen._term_input_file :(
510-
511-
# XXX no stdin for you! What to do here?
512-
#sys.stdin = None #FakeStdin(myrepl)
513-
sys.stdout = myrepl
514-
sys.stderr = myrepl
515-
516-
# This needs more thought. What needs to happen inside the mainloop?
517-
# Note that we need the mainloop started before our stdio
518-
# redirection is hit.
519-
def start(main_loop, user_data):
520-
if exec_args:
521-
bpargs.exec_code(interpreter, exec_args)
522-
if not options.interactive:
523-
raise urwid.ExitMainLoop()
524-
if not exec_args:
525-
sys.path.insert(0, '')
526-
# this is CLIRepl.startup inlined.
527-
filename = os.environ.get('PYTHONSTARTUP')
528-
if filename and os.path.isfile(filename):
529-
with open(filename, 'r') as f:
530-
if py3:
531-
interpreter.runsource(f.read(), filename, 'exec')
532-
else:
533-
interpreter.runsource(f.read(), filename, 'exec',
534-
encode=False)
535-
536-
if banner is not None:
537-
repl.write(banner)
538-
repl.write('\n')
539-
myrepl.start()
540-
541-
# This bypasses main_loop.set_alarm_in because we must *not*
542-
# hit the draw_screen call (it's unnecessary and slow).
543-
def run_find_coroutine():
544-
if find_coroutine():
545-
main_loop.event_loop.alarm(0, run_find_coroutine)
546-
547-
run_find_coroutine()
548-
549-
loop.set_alarm_in(0, start)
550-
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
565-
566-
if config.hist_length:
567-
histfilename = os.path.expanduser(config.hist_file)
568-
myrepl.rl_history.save(histfilename,
569-
locale.getpreferredencoding())
570-
finally:
571-
sys.stdin = orig_stdin
572-
sys.stderr = orig_stderr
573-
sys.stdout = orig_stdout
505+
# urwid's screen start() and stop() calls currently hit sys.stdin
506+
# directly (via RealTerminal.tty_signal_keys), so start the screen
507+
# before swapping sys.std*, and swap them back before restoring
508+
# the screen. This also avoids crashes if our redirected sys.std*
509+
# are called before we get around to starting the mainloop
510+
# (urwid raises an exception if we try to draw to the screen
511+
# before starting it).
512+
def run_with_screen_before_mainloop():
513+
try:
514+
# XXX no stdin for you! What to do here?
515+
sys.stdin = None #FakeStdin(myrepl)
516+
sys.stdout = myrepl
517+
sys.stderr = myrepl
518+
519+
loop.set_alarm_in(0, start)
520+
521+
while True:
522+
try:
523+
loop.run()
524+
except KeyboardInterrupt:
525+
# HACK: if we run under a twisted mainloop this should
526+
# never happen: we have a SIGINT handler set.
527+
# If we use the urwid select-based loop we just restart
528+
# that loop if interrupted, instead of trying to cook
529+
# up an equivalent to reactor.callFromThread (which
530+
# is what our Twisted sigint handler does)
531+
loop.set_alarm_in(
532+
0, lambda *args: myrepl.keyboard_interrupt())
533+
continue
534+
break
535+
536+
if config.hist_length:
537+
histfilename = os.path.expanduser(config.hist_file)
538+
myrepl.rl_history.save(histfilename,
539+
locale.getpreferredencoding())
540+
541+
finally:
542+
sys.stdin = orig_stdin
543+
sys.stderr = orig_stderr
544+
sys.stdout = orig_stdout
545+
546+
# This needs more thought. What needs to happen inside the mainloop?
547+
def start(main_loop, user_data):
548+
if exec_args:
549+
bpargs.exec_code(interpreter, exec_args)
550+
if not options.interactive:
551+
raise urwid.ExitMainLoop()
552+
if not exec_args:
553+
sys.path.insert(0, '')
554+
# this is CLIRepl.startup inlined.
555+
filename = os.environ.get('PYTHONSTARTUP')
556+
if filename and os.path.isfile(filename):
557+
with open(filename, 'r') as f:
558+
if py3:
559+
interpreter.runsource(f.read(), filename, 'exec')
560+
else:
561+
interpreter.runsource(f.read(), filename, 'exec',
562+
encode=False)
563+
564+
if banner is not None:
565+
repl.write(banner)
566+
repl.write('\n')
567+
myrepl.start()
568+
569+
# This bypasses main_loop.set_alarm_in because we must *not*
570+
# hit the draw_screen call (it's unnecessary and slow).
571+
def run_find_coroutine():
572+
if find_coroutine():
573+
main_loop.event_loop.alarm(0, run_find_coroutine)
574+
575+
run_find_coroutine()
576+
577+
loop.screen.run_wrapper(run_with_screen_before_mainloop)
574578

575579
if config.flush_output and not options.quiet:
576580
sys.stdout.write(myrepl.getstdout())

0 commit comments

Comments
 (0)