Example #1
0
 def setUp(self):
     self.refresh_requests = []
     self.repl = Repl(banner='', config=setup_config(),
                      request_refresh=self.refresh)
     # clear history
     self.repl.rl_history = History()
     self.repl.height, self.repl.width = (5, 32)
class TestCurtsiesPainting(FormatStringTest):
    def setUp(self):
        self.repl = Repl(config=setup_config())
        self.repl.rl_history = History() # clear history
        self.repl.height, self.repl.width = (5, 10)

    def assert_paint(self, screen, cursor_row_col):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqual(array, screen)
        self.assertEqual(cursor_pos, cursor_row_col)

    def assert_paint_ignoring_formatting(self, screen, cursor_row_col=None):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqualIgnoringFormatting(array, screen)
        if cursor_row_col is not None:
            self.assertEqual(cursor_pos, cursor_row_col)
Example #3
0
class TestCurtsiesPainting(FormatStringTest):
    def setUp(self):
        self.repl = Repl(config=setup_config())
        self.repl.rl_history = History() # clear history
        self.repl.height, self.repl.width = (5, 10)

    def assert_paint(self, screen, cursor_row_col):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqual(array, screen)
        self.assertEqual(cursor_pos, cursor_row_col)

    def assert_paint_ignoring_formatting(self, screen, cursor_row_col=None):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqualIgnoringFormatting(array, screen)
        if cursor_row_col is not None:
            self.assertEqual(cursor_pos, cursor_row_col)
Example #4
0
class TestCurtsiesPainting(FormatStringTest):
    def setUp(self):
        self.repl = Repl(config=setup_config())
        self.repl.rl_history = History()  # clear history
        self.repl.height, self.repl.width = (5, 10)

    def assert_paint(self, screen, cursor_row_col):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqual(array, screen)
        self.assertEqual(cursor_pos, cursor_row_col)

    def assert_paint_ignoring_formatting(self, screen, cursor_row_col):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqualIgnoringFormatting(array, screen)

    def test_startup(self):
        screen = fsarray([cyan('>>> '), cyan('Welcome to')])
        self.assert_paint(screen, (0, 4))

    def test_enter_text(self):
        [self.repl.add_normal_character(c) for c in '1 + 1']
        screen = fsarray([
            cyan('>>> ') + bold(
                green('1') + cyan(' ') + yellow('+') + cyan(' ') + green('1')),
            cyan('Welcome to')
        ])
        self.assert_paint(screen, (0, 9))

    def test_run_line(self):
        try:
            orig_stdout = sys.stdout
            sys.stdout = self.repl.stdout
            [self.repl.add_normal_character(c) for c in '1 + 1']
            self.repl.on_enter()
            screen = fsarray([u'>>> 1 + 1', '2', 'Welcome to'])
            self.assert_paint_ignoring_formatting(screen, (0, 9))
        finally:
            sys.stdout = orig_stdout

    def test_completion(self):
        self.repl.height, self.repl.width = (5, 32)
        self.repl.current_line = 'se'
        self.cursor_offset = 2
        screen = [
            u'>>> se', u'┌───────────────────────┐',
            u'│ set(     setattr(     │', u'└───────────────────────┘', u'',
            u'Welcome to bpython! Press <F1> f'
        ]
        self.assert_paint_ignoring_formatting(screen, (0, 9))
class TestCurtsiesPainting(FormatStringTest):
    def setUp(self):
        self.repl = Repl(config=setup_config())
        self.repl.rl_history = History() # clear history
        self.repl.height, self.repl.width = (5, 10)

    def assert_paint(self, screen, cursor_row_col):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqual(array, screen)
        self.assertEqual(cursor_pos, cursor_row_col)

    def assert_paint_ignoring_formatting(self, screen, cursor_row_col):
        array, cursor_pos = self.repl.paint()
        self.assertFSArraysEqualIgnoringFormatting(array, screen)

    def test_startup(self):
        screen = fsarray([cyan('>>> '), cyan('Welcome to')])
        self.assert_paint(screen, (0, 4))

    def test_enter_text(self):
        [self.repl.add_normal_character(c) for c in '1 + 1']
        screen = fsarray([cyan('>>> ') + bold(green('1')+cyan(' ')+
                          yellow('+') + cyan(' ') + green('1')), cyan('Welcome to')])
        self.assert_paint(screen, (0, 9))

    def test_run_line(self):
        try:
            orig_stdout = sys.stdout
            sys.stdout = self.repl.stdout
            [self.repl.add_normal_character(c) for c in '1 + 1']
            self.repl.on_enter()
            screen = fsarray([u'>>> 1 + 1', '2', 'Welcome to'])
            self.assert_paint_ignoring_formatting(screen, (0, 9))
        finally:
            sys.stdout = orig_stdout

    def test_completion(self):
        self.repl.height, self.repl.width = (5, 32)
        self.repl.current_line = 'se'
        self.cursor_offset = 2
        screen = [u'>>> se',
                  u'┌───────────────────────┐',
                  u'│ set(     setattr(     │',
                  u'└───────────────────────┘',
                  u'',
                  u'Welcome to bpython! Press <F1> f']
        self.assert_paint_ignoring_formatting(screen, (0, 9))
Example #6
0
def mainloop(config,
             locals_,
             banner,
             interp=None,
             paste=None,
             interactive=True):
    with curtsies.input.Input(keynames='curtsies',
                              sigint_event=True) as input_generator:
        with curtsies.window.CursorAwareWindow(
                sys.stdout,
                sys.stdin,
                keep_last_line=True,
                hide_cursor=False,
                extra_bytes_callback=input_generator.unget_bytes) as window:

            reload_requests = []

            def request_reload(desc):
                reload_requests.append(curtsies.events.ReloadEvent([desc]))

            refresh_requests = []

            def request_refresh(when='now'):
                refresh_requests.append(
                    curtsies.events.RefreshRequestEvent(when=when))

            def event_or_refresh(timeout=None):
                while True:
                    t = time.time()
                    refresh_requests.sort(
                        key=lambda r: 0 if r.when == 'now' else r.when)
                    if refresh_requests and (refresh_requests[0].when == 'now'
                                             or refresh_requests[-1].when < t):
                        yield refresh_requests.pop(0)
                    elif reload_requests:
                        e = reload_requests.pop()
                        yield e
                    else:
                        e = input_generator.send(.2)
                        if e is not None:
                            yield e

            global repl  # global for easy introspection `from bpython.curtsies import repl`
            with Repl(config=config,
                      locals_=locals_,
                      request_refresh=request_refresh,
                      request_reload=request_reload,
                      get_term_hw=window.get_term_hw,
                      get_cursor_vertical_diff=window.get_cursor_vertical_diff,
                      banner=banner,
                      interp=interp,
                      interactive=interactive,
                      orig_tcattrs=input_generator.original_stty) as repl:
                repl.height, repl.width = window.t.height, window.t.width

                def process_event(e):
                    """If None is passed in, just paint the screen"""
                    try:
                        if e is not None:
                            repl.process_event(e)
                    except (SystemExitFromCodeGreenlet, SystemExit) as err:
                        array, cursor_pos = repl.paint(
                            about_to_exit=True,
                            user_quit=isinstance(err,
                                                 SystemExitFromCodeGreenlet))
                        scrolled = window.render_to_terminal(array, cursor_pos)
                        repl.scroll_offset += scrolled
                        raise
                    else:
                        array, cursor_pos = repl.paint()
                        scrolled = window.render_to_terminal(array, cursor_pos)
                        repl.scroll_offset += scrolled

                if paste:
                    process_event(paste)

                process_event(
                    None
                )  #priming the pump (do a display before waiting for first event)
                for _, e in izip(find_iterator, event_or_refresh(0)):
                    if e is not None:
                        process_event(e)
                for e in event_or_refresh():
                    process_event(e)
 def setUp(self):
     self.repl = Repl(config=setup_config())
     # clear history
     self.repl.rl_history = History()
     self.repl.height, self.repl.width = (5, 10)
class TestCurtsiesRewindRedraw(CurtsiesPaintingTest):
    def refresh(self):
        self.refresh_requests.append(RefreshRequestEvent())

    def send_refreshes(self):
        while self.refresh_requests:
            self.repl.process_event(self.refresh_requests.pop())
            _, _ = self.repl.paint()

    def enter(self, line=None):
        """Enter a line of text, avoiding autocompletion windows

        autocomplete could still happen if the entered line has
        autocompletion that would happen then, but intermediate
        stages won't happen"""
        if line is not None:
            self.repl._set_cursor_offset(len(line), update_completion=False)
            self.repl.current_line = line
        with output_to_repl(self.repl):
            self.repl.on_enter(insert_into_history=False)
            self.assertEqual(self.repl.rl_history.entries, [''])
            self.send_refreshes()

    def undo(self):
        with output_to_repl(self.repl):
            self.repl.undo()
            self.send_refreshes()

    def setUp(self):
        self.refresh_requests = []
        self.repl = Repl(banner='',
                         config=setup_config(),
                         request_refresh=self.refresh)
        # clear history
        self.repl.rl_history = History()
        self.repl.height, self.repl.width = (5, 32)

    def test_rewind(self):
        self.repl.current_line = '1 + 1'
        self.enter()
        screen = ['>>> 1 + 1', '2', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))
        self.repl.undo()
        screen = ['>>> ']
        self.assert_paint_ignoring_formatting(screen, (0, 4))

    def test_rewind_contiguity_loss(self):
        self.enter('1 + 1')
        self.enter('2 + 2')
        self.enter('def foo(x):')
        self.repl.current_line = '    return x + 1'
        screen = [
            '>>> 1 + 1', '2', '>>> 2 + 2', '4', '>>> def foo(x):',
            '...     return x + 1'
        ]
        self.assert_paint_ignoring_formatting(screen, (5, 8))
        self.repl.scroll_offset = 1
        self.assert_paint_ignoring_formatting(screen[1:], (4, 8))
        self.undo()
        screen = ['2', '>>> 2 + 2', '4', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (3, 4))
        self.undo()
        screen = ['2', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (1, 4))
        self.undo()
        screen = [
            CONTIGUITY_BROKEN_MSG[:self.repl.width], '>>> ', '', '', '', ' '
        ]  # TODO why is that there? Necessary?
        self.assert_paint_ignoring_formatting(screen, (1, 4))
        screen = ['>>> ']
        self.assert_paint_ignoring_formatting(screen, (0, 4))

    def test_inconsistent_history_doesnt_happen_if_onscreen(self):
        self.enter('1 + 1')
        screen = ['>>> 1 + 1', '2', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))
        self.enter("2 + 2")
        screen = ['>>> 1 + 1', '2', '>>> 2 + 2', '4', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.display_lines[0] = self.repl.display_lines[0] * 2
        self.undo()
        screen = ['>>> 1 + 1', '2', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))

    def test_rewind_inconsistent_history(self):
        self.enter('1 + 1')
        self.enter('2 + 2')
        self.enter('3 + 3')
        screen = ['>>> 1 + 1', '2', '>>> 2 + 2', '4', '>>> 3 + 3', '6', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (6, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[2:], (4, 4))
        self.repl.display_lines[0] = self.repl.display_lines[0] * 2
        self.undo()
        screen = [
            INCONSISTENT_HISTORY_MSG[:self.repl.width], '>>> 2 + 2', '4',
            '>>> ', '', ' '
        ]
        self.assert_paint_ignoring_formatting(screen, (3, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:-2], (2, 4))
        self.assert_paint_ignoring_formatting(screen[1:-2], (2, 4))

    def test_rewind_inconsistent_history_more_lines_same_screen(self):
        self.repl.width = 60
        sys.a = 5
        self.enter('import sys')
        self.enter('for i in range(sys.a):')
        self.enter('    print(sys.a)')
        self.enter('')
        self.enter('1 + 1')
        self.enter('2 + 2')
        screen = [
            '>>> import sys', '>>> for i in range(sys.a):',
            '...     print(sys.a)', '... ', '5', '5', '5', '5', '5',
            '>>> 1 + 1', '2', '>>> 2 + 2', '4', '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen, (13, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[9:], (4, 4))
        sys.a = 6
        self.undo()
        screen = [
            INCONSISTENT_HISTORY_MSG[:self.repl.width],
            '6',
            # everything will jump down a line - that's perfectly
            # reasonable
            '>>> 1 + 1',
            '2',
            '>>> ',
            ' '
        ]
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:-1], (3, 4))

    def test_rewind_inconsistent_history_more_lines_lower_screen(self):
        self.repl.width = 60
        sys.a = 5
        self.enter("import sys")
        self.enter("for i in range(sys.a):")
        self.enter("    print(sys.a)")
        self.enter("")
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [
            ">>> import sys", ">>> for i in range(sys.a):",
            "...     print(sys.a)", '... ', '5', '5', '5', '5', '5',
            '>>> 1 + 1', '2', '>>> 2 + 2', '4', '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen, (13, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[9:], (4, 4))
        sys.a = 8
        self.undo()
        screen = [
            INCONSISTENT_HISTORY_MSG[:self.repl.width], '8', '8', '8',
            '>>> 1 + 1', '2', '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen)
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[-5:])

    def test_rewind_inconsistent_history_more_lines_raise_screen(self):
        self.repl.width = 60
        sys.a = 5
        self.enter("import sys")
        self.enter("for i in range(sys.a):")
        self.enter("    print(sys.a)")
        self.enter("")
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [
            ">>> import sys", ">>> for i in range(sys.a):",
            "...     print(sys.a)", '... ', '5', '5', '5', '5', '5',
            '>>> 1 + 1', '2', '>>> 2 + 2', '4', '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen, (13, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[9:], (4, 4))
        sys.a = 1
        self.undo()
        screen = [
            INCONSISTENT_HISTORY_MSG[:self.repl.width], '1', '>>> 1 + 1', '2',
            '>>> ', ' '
        ]
        self.assert_paint_ignoring_formatting(screen)
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:-1])

    def test_rewind_history_not_quite_inconsistent(self):
        self.repl.width = 50
        sys.a = 5
        self.enter("for i in range(__import__('sys').a):")
        self.enter("    print(i)")
        self.enter("")
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [
            ">>> for i in range(__import__('sys').a):", "...     print(i)",
            "... ", '0', '1', '2', '3', '4', '>>> 1 + 1', '2', '>>> 2 + 2',
            '4', '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen, (12, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[8:], (4, 4))
        sys.a = 6
        self.undo()
        screen = [
            '5',
            # everything will jump down a line - that's perfectly
            # reasonable
            '>>> 1 + 1',
            '2',
            '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen, (3, 4))

    def test_rewind_barely_consistent(self):
        self.enter("1 + 1")
        self.enter("2 + 2")
        self.enter("3 + 3")
        screen = [">>> 1 + 1", '2', '>>> 2 + 2', '4', '>>> 3 + 3', '6', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (6, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[2:], (4, 4))
        self.repl.display_lines[2] = self.repl.display_lines[2] * 2
        self.undo()
        screen = ['>>> 2 + 2', '4', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))

    def test_clear_screen(self):
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [">>> 1 + 1", '2', '>>> 2 + 2', '4', '>>> ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.request_paint_to_clear_screen = True
        screen = [">>> 1 + 1", '2', '>>> 2 + 2', '4', '>>> ', '', '', '', '']
        self.assert_paint_ignoring_formatting(screen, (4, 4))

    def test_scroll_down_while_banner_visible(self):
        self.repl.status_bar.message('STATUS_BAR')
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [
            ">>> 1 + 1", '2', '>>> 2 + 2', '4', '>>> ',
            'STATUS_BAR                      '
        ]
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:], (3, 4))

    def test_clear_screen_while_banner_visible(self):
        self.repl.status_bar.message('STATUS_BAR')
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [
            ">>> 1 + 1", '2', '>>> 2 + 2', '4', '>>> ',
            'STATUS_BAR                      '
        ]
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:], (3, 4))

        self.repl.request_paint_to_clear_screen = True
        screen = [
            '2', '>>> 2 + 2', '4', '>>> ', '', '', '',
            'STATUS_BAR                      '
        ]
        self.assert_paint_ignoring_formatting(screen, (3, 4))

    def test_cursor_stays_at_bottom_of_screen(self):
        """infobox showing up during intermediate render was causing this to
        fail, #371"""
        self.repl.width = 50
        self.repl.current_line = "__import__('random').__name__"
        with output_to_repl(self.repl):
            self.repl.on_enter(insert_into_history=False)
        screen = [">>> __import__('random').__name__", "'random'"]
        self.assert_paint_ignoring_formatting(screen)

        with output_to_repl(self.repl):
            self.repl.process_event(self.refresh_requests.pop())
        screen = [">>> __import__('random').__name__", "'random'", ""]
        self.assert_paint_ignoring_formatting(screen)

        with output_to_repl(self.repl):
            self.repl.process_event(self.refresh_requests.pop())
        screen = [">>> __import__('random').__name__", "'random'", ">>> "]
        self.assert_paint_ignoring_formatting(screen, (2, 4))

    def test_unhighlight_paren_bugs(self):
        """two previous bugs, paren did't highlight until next render
        and paren didn't unhighlight until enter"""
        self.assertEqual(self.repl.rl_history.entries, [''])
        self.enter('(')
        self.assertEqual(self.repl.rl_history.entries, [''])
        screen = [">>> (", "... "]
        self.assertEqual(self.repl.rl_history.entries, [''])
        self.assert_paint_ignoring_formatting(screen)
        self.assertEqual(self.repl.rl_history.entries, [''])

        with output_to_repl(self.repl):
            self.assertEqual(self.repl.rl_history.entries, [''])
            self.repl.process_event(')')
            self.assertEqual(self.repl.rl_history.entries, [''])
        screen = fsarray([
            cyan(">>> ") + on_magenta(bold(red('('))),
            green("... ") + on_magenta(bold(red(')')))
        ])
        self.assert_paint(screen, (1, 5))

        with output_to_repl(self.repl):
            self.repl.process_event(' ')
        screen = fsarray([
            cyan(">>> ") + yellow('('),
            green("... ") + yellow(')') + bold(cyan(" "))
        ])
        self.assert_paint(screen, (1, 6))

    def send_key(self, key):
        self.repl.process_event('<SPACE>' if key == ' ' else key)
        self.repl.paint()  # has some side effects we need to be wary of

    def test_472(self):
        [self.send_key(c) for c in "(1, 2, 3)"]
        with output_to_repl(self.repl):
            self.send_key('\n')
            self.send_refreshes()
            self.send_key('<UP>')
            self.repl.paint()
            [self.send_key('<LEFT>') for _ in range(4)]
            self.send_key('<BACKSPACE>')
            self.send_key('4')
            self.repl.on_enter()
            self.send_refreshes()
        screen = [
            ">>> (1, 2, 3)", '(1, 2, 3)', '>>> (1, 4, 3)', '(1, 4, 3)', '>>> '
        ]
        self.assert_paint_ignoring_formatting(screen, (4, 4))
Example #9
0
def mainloop(config,
             locals_,
             banner,
             interp=None,
             paste=None,
             interactive=True):
    with curtsies.input.Input(keynames='curtsies', sigint_event=True) as \
            input_generator:
        with curtsies.window.CursorAwareWindow(
                sys.stdout,
                sys.stdin,
                keep_last_line=True,
                hide_cursor=False,
                extra_bytes_callback=input_generator.unget_bytes) as window:

            request_refresh = input_generator.event_trigger(
                bpythonevents.RefreshRequestEvent)
            schedule_refresh = input_generator.scheduled_event_trigger(
                bpythonevents.ScheduledRefreshRequestEvent)
            request_reload = input_generator.threadsafe_event_trigger(
                bpythonevents.ReloadEvent)
            interrupting_refresh = input_generator.threadsafe_event_trigger(
                lambda: None)
            request_undo = input_generator.event_trigger(
                bpythonevents.UndoEvent)

            def on_suspend():
                window.__exit__(None, None, None)
                input_generator.__exit__(None, None, None)

            def after_suspend():
                input_generator.__enter__()
                window.__enter__()
                interrupting_refresh()

            # global for easy introspection `from bpython.curtsies import repl`
            global repl
            with Repl(config=config,
                      locals_=locals_,
                      request_refresh=request_refresh,
                      schedule_refresh=schedule_refresh,
                      request_reload=request_reload,
                      request_undo=request_undo,
                      get_term_hw=window.get_term_hw,
                      get_cursor_vertical_diff=window.get_cursor_vertical_diff,
                      banner=banner,
                      interp=interp,
                      interactive=interactive,
                      orig_tcattrs=input_generator.original_stty,
                      on_suspend=on_suspend,
                      after_suspend=after_suspend) as repl:
                repl.height, repl.width = window.t.height, window.t.width

                def process_event(e):
                    """If None is passed in, just paint the screen"""
                    try:
                        if e is not None:
                            repl.process_event(e)
                    except (SystemExitFromCodeGreenlet, SystemExit) as err:
                        array, cursor_pos = repl.paint(
                            about_to_exit=True,
                            user_quit=isinstance(err,
                                                 SystemExitFromCodeGreenlet))
                        scrolled = window.render_to_terminal(array, cursor_pos)
                        repl.scroll_offset += scrolled
                        raise
                    else:
                        array, cursor_pos = repl.paint()
                        scrolled = window.render_to_terminal(array, cursor_pos)
                        repl.scroll_offset += scrolled

                if interactive:
                    # Add custom help command
                    # TODO: add methods to run the code
                    repl.coderunner.interp.locals['_repl'] = repl

                    repl.coderunner.interp.runsource(
                        'from bpython.curtsiesfrontend._internal '
                        'import _Helper')
                    repl.coderunner.interp.runsource('help = _Helper(_repl)\n')

                    del repl.coderunner.interp.locals['_repl']
                    del repl.coderunner.interp.locals['_Helper']

                    # run startup file
                    process_event(bpythonevents.RunStartupFileEvent())

                # handle paste
                if paste:
                    process_event(paste)

                # do a display before waiting for first event
                process_event(None)
                for unused in find_iterator:
                    e = input_generator.send(0)
                    if e is not None:
                        process_event(e)

                for e in input_generator:
                    process_event(e)
 def setUp(self):
     self.repl = Repl(config=setup_config())
     # clear history
     self.repl.rl_history = History()
     self.repl.height, self.repl.width = (5, 10)
class TestCurtsiesRewindRedraw(CurtsiesPaintingTest):
    def refresh(self):
        self.refresh_requests.append(RefreshRequestEvent())

    def send_refreshes(self):
        while self.refresh_requests:
            self.repl.process_event(self.refresh_requests.pop())
            _, _ = self.repl.paint()

    def enter(self, line=None):
        """Enter a line of text, avoiding autocompletion windows

        autocomplete could still happen if the entered line has
        autocompletion that would happen then, but intermediate
        stages won't happen"""
        if line is not None:
            self.repl.current_line = line
        with output_to_repl(self.repl):
            self.repl.on_enter(insert_into_history=False)
            self.assertEqual(self.repl.rl_history.entries, [''])
            self.send_refreshes()

    def undo(self):
        with output_to_repl(self.repl):
            self.repl.undo()
            self.send_refreshes()

    def setUp(self):
        self.refresh_requests = []
        self.repl = Repl(banner='', config=setup_config(),
                         request_refresh=self.refresh)
        # clear history
        self.repl.rl_history = History()
        self.repl.height, self.repl.width = (5, 32)

    def test_rewind(self):
        self.repl.current_line = '1 + 1'
        self.enter()
        screen = [u'>>> 1 + 1',
                  u'2',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))
        self.repl.undo()
        screen = [u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (0, 4))

    def test_rewind_contiguity_loss(self):
        self.enter('1 + 1')
        self.enter('2 + 2')
        self.enter('def foo(x):')
        self.repl.current_line = '    return x + 1'
        screen = [u'>>> 1 + 1',
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> def foo(x):',
                  u'...     return x + 1']
        self.assert_paint_ignoring_formatting(screen, (5, 8))
        self.repl.scroll_offset = 1
        self.assert_paint_ignoring_formatting(screen[1:], (4, 8))
        self.undo()
        screen = [u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (3, 4))
        self.undo()
        screen = [u'2',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (1, 4))
        self.undo()
        screen = [CONTIGUITY_BROKEN_MSG[:self.repl.width],
                  u'>>> ',
                  u'',
                  u'',
                  u'',
                  u' ']  # TODO why is that there? Necessary?
        self.assert_paint_ignoring_formatting(screen, (1, 4))
        screen = [u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (0, 4))

    def test_inconsistent_history_doesnt_happen_if_onscreen(self):
        self.enter("1 + 1")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))
        self.enter("2 + 2")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.display_lines[0] = self.repl.display_lines[0] * 2
        self.undo()
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))

    def test_rewind_inconsistent_history(self):
        self.enter("1 + 1")
        self.enter("2 + 2")
        self.enter("3 + 3")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> 3 + 3',
                  u'6',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (6, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[2:], (4, 4))
        self.repl.display_lines[0] = self.repl.display_lines[0] * 2
        self.undo()
        screen = [INCONSISTENT_HISTORY_MSG[:self.repl.width],
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ',
                  u'',
                  u' ']
        self.assert_paint_ignoring_formatting(screen, (3, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:-2], (2, 4))
        self.assert_paint_ignoring_formatting(screen[1:-2], (2, 4))

    def test_rewind_inconsistent_history_more_lines_same_screen(self):
        self.repl.width = 60
        sys.a = 5
        self.enter("import sys")
        self.enter("for i in range(sys.a): print(sys.a)")
        self.enter()
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> import sys",
                  u">>> for i in range(sys.a): print(sys.a)",
                  u'... ',
                  u'5',
                  u'5',
                  u'5',
                  u'5',
                  u'5',
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (12, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[8:], (4, 4))
        sys.a = 6
        self.undo()
        screen = [INCONSISTENT_HISTORY_MSG[:self.repl.width],
                  u'6',
                  # everything will jump down a line - that's perfectly
                  # reasonable
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> ',
                  u' ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:-1], (3, 4))

    def test_rewind_inconsistent_history_more_lines_lower_screen(self):
        self.repl.width = 60
        sys.a = 5
        self.enter("import sys")
        self.enter("for i in range(sys.a): print(sys.a)")
        self.enter()
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> import sys",
                  u">>> for i in range(sys.a): print(sys.a)",
                  u'... ',
                  u'5',
                  u'5',
                  u'5',
                  u'5',
                  u'5',
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (12, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[8:], (4, 4))
        sys.a = 8
        self.undo()
        screen = [INCONSISTENT_HISTORY_MSG[:self.repl.width],
                  u'8',
                  u'8',
                  u'8',
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen)
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[-5:])

    def test_rewind_inconsistent_history_more_lines_raise_screen(self):
        self.repl.width = 60
        sys.a = 5
        self.enter("import sys")
        self.enter("for i in range(sys.a): print(sys.a)")
        self.enter()
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> import sys",
                  u">>> for i in range(sys.a): print(sys.a)",
                  u'... ',
                  u'5',
                  u'5',
                  u'5',
                  u'5',
                  u'5',
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (12, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[8:], (4, 4))
        sys.a = 1
        self.undo()
        screen = [INCONSISTENT_HISTORY_MSG[:self.repl.width],
                  u'1',
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> ',
                  u' ']
        self.assert_paint_ignoring_formatting(screen)
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:-1])

    def test_rewind_history_not_quite_inconsistent(self):
        self.repl.width = 50
        sys.a = 5
        self.enter("for i in range(__import__('sys').a): print(i)")
        self.enter()
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> for i in range(__import__('sys').a): print(i)",
                  u'... ',
                  u'0',
                  u'1',
                  u'2',
                  u'3',
                  u'4',
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (11, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[7:], (4, 4))
        sys.a = 6
        self.undo()
        screen = [u'5',
                  # everything will jump down a line - that's perfectly
                  # reasonable
                  u'>>> 1 + 1',
                  u'2',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (3, 4))

    def test_rewind_barely_consistent(self):
        self.enter("1 + 1")
        self.enter("2 + 2")
        self.enter("3 + 3")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> 3 + 3',
                  u'6',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (6, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[2:], (4, 4))
        self.repl.display_lines[2] = self.repl.display_lines[2] * 2
        self.undo()
        screen = [u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (2, 4))

    def test_clear_screen(self):
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.request_paint_to_clear_screen = True
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ', u'', u'', u'', u'']
        self.assert_paint_ignoring_formatting(screen, (4, 4))

    def test_scroll_down_while_banner_visible(self):
        self.repl.status_bar.message('STATUS_BAR')
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ',
                  u'STATUS_BAR                      ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:], (3, 4))

    def test_clear_screen_while_banner_visible(self):
        self.repl.status_bar.message('STATUS_BAR')
        self.enter("1 + 1")
        self.enter("2 + 2")
        screen = [u">>> 1 + 1",
                  u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ',
                  u'STATUS_BAR                      ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))
        self.repl.scroll_offset += len(screen) - self.repl.height
        self.assert_paint_ignoring_formatting(screen[1:], (3, 4))

        self.repl.request_paint_to_clear_screen = True
        screen = [u'2',
                  u'>>> 2 + 2',
                  u'4',
                  u'>>> ',
                  u'', u'', u'',
                  u'STATUS_BAR                      ']
        self.assert_paint_ignoring_formatting(screen, (3, 4))

    def test_cursor_stays_at_bottom_of_screen(self):
        """infobox showing up during intermediate render was causing this to
        fail, #371"""
        self.repl.width = 50
        self.repl.current_line = "__import__('random').__name__"
        with output_to_repl(self.repl):
            self.repl.on_enter(insert_into_history=False)
        screen = [u">>> __import__('random').__name__",
                  u"'random'"]
        self.assert_paint_ignoring_formatting(screen)

        with output_to_repl(self.repl):
            self.repl.process_event(self.refresh_requests.pop())
        screen = [u">>> __import__('random').__name__",
                  u"'random'",
                  u""]
        self.assert_paint_ignoring_formatting(screen)

        with output_to_repl(self.repl):
            self.repl.process_event(self.refresh_requests.pop())
        screen = [u">>> __import__('random').__name__",
                  u"'random'",
                  u">>> "]
        self.assert_paint_ignoring_formatting(screen, (2, 4))

    def test_unhighlight_paren_bugs(self):
        """two previous bugs, paren did't highlight until next render
        and paren didn't unhighlight until enter"""
        self.assertEqual(self.repl.rl_history.entries, [''])
        self.enter('(')
        self.assertEqual(self.repl.rl_history.entries, [''])
        screen = [u">>> (",
                  u"... "]
        self.assertEqual(self.repl.rl_history.entries, [''])
        self.assert_paint_ignoring_formatting(screen)
        self.assertEqual(self.repl.rl_history.entries, [''])

        with output_to_repl(self.repl):
            self.assertEqual(self.repl.rl_history.entries, [''])
            self.repl.process_event(')')
            self.assertEqual(self.repl.rl_history.entries, [''])
        screen = fsarray([cyan(u">>> ")+on_magenta(bold(red('('))),
                         green(u"... ")+on_magenta(bold(red(')')))])
        self.assert_paint(screen, (1, 5))

        with output_to_repl(self.repl):
            self.repl.process_event(' ')
        screen = fsarray([cyan(u">>> ")+yellow('('),
                         green(u"... ")+yellow(')')+bold(cyan(" "))])
        self.assert_paint(screen, (1, 6))

    def send_key(self, key):
        self.repl.process_event(u'<SPACE>' if key == ' ' else key)
        self.repl.paint()  # has some side effects we need to be wary of

    def test_472(self):
        [self.send_key(c) for c in "(1, 2, 3)"]
        with output_to_repl(self.repl):
            self.send_key('\n')
            self.send_refreshes()
            self.send_key('<UP>')
            self.repl.paint()
            [self.send_key('<LEFT>') for _ in range(4)]
            self.send_key('<BACKSPACE>')
            self.send_key('4')
            self.repl.on_enter()
            self.send_refreshes()
        screen = [">>> (1, 2, 3)",
                  '(1, 2, 3)',
                  '>>> (1, 4, 3)',
                  '(1, 4, 3)',
                  '>>> ']
        self.assert_paint_ignoring_formatting(screen, (4, 4))