def _feed_cli_with_input(text, editing_mode=EditingMode.EMACS, clipboard=None, history=None): """ Create a CommandLineInterface, feed it with the given user input and return the CLI object. This returns a (result, CLI) tuple. """ # If the given text doesn't end with a newline, the interface won't finish. assert text.endswith('\n') loop = PosixEventLoop() try: inp = PipeInput() inp.send_text(text) cli = CommandLineInterface( application=Application( buffer=Buffer(accept_action=AcceptAction.RETURN_DOCUMENT, history=history), editing_mode=editing_mode, clipboard=clipboard or InMemoryClipboard(), key_bindings_registry=KeyBindingManager.for_prompt().registry, ), eventloop=loop, input=inp, output=DummyOutput()) result = cli.run() return result, cli finally: loop.close() inp.close()
async def td_client_interactive_shell(callback): eventloop = create_asyncio_eventloop() key_bindings_manager = KeyBindingManager.for_prompt() load_td_client_kay_bindings(key_bindings_manager=key_bindings_manager) cli = CommandLineInterface( application=create_prompt_application('待开发指令,just echo: ', key_bindings_registry=key_bindings_manager.registry, get_bottom_toolbar_tokens=get_td_client_bottom_toolbar_tokens, style=get_td_client_styles() ), eventloop=eventloop ) sys.stdout = cli.stdout_proxy() while True: try: result = await cli.run_async() if callback is not None: resp = callback(user_input=result.text) print(resp) else: print('You said: "{0}"'.format(result.text)) except (EOFError, KeyboardInterrupt): return
async def interactive_shell(): """ Like `interactive_shell`, but doing things manual. """ # Create an asyncio `EventLoop` object. This is a wrapper around the # asyncio loop that can be passed into prompt_toolkit. eventloop = create_asyncio_eventloop() # Create interface. cli = CommandLineInterface( application=create_prompt_application('Say something inside the event loop: '), eventloop=eventloop ) # Patch stdout in something that will always print *above* the prompt when # something is written to stdout. sys.stdout = cli.stdout_proxy() # Run echo loop. Read text from stdin, and reply it back. while True: try: result = await cli.run_async() print('You said: "{0}"'.format(result.text)) except (EOFError, KeyboardInterrupt): return
def interactive_shell(): """ Coroutine that shows the interactive command line. """ # Create an asyncio `EventLoop` object. This is a wrapper around the # asyncio loop that can be passed into prompt_toolkit. eventloop = create_asyncio_eventloop() # Create interface. cli = CommandLineInterface( application=create_default_application('Say something inside the event loop: '), eventloop=eventloop) # Patch stdout in something that will always print *above* the prompt when # something is written to stdout. sys.stdout = cli.stdout_proxy() # Run echo loop. Read text from stdin, and reply it back. while True: try: result = yield from cli.run_async() print('You said: "%s"\n' % result.text) except (EOFError, KeyboardInterrupt): loop.stop() print('Qutting event loop. Bye.') return
def main(): eventloop = create_eventloop() done = [False] # Non local def on_read_start(cli): """ This function is called when we start reading at the input. (Actually the start of the read-input event loop.) """ # Following function should be run in the background. # We do it by using an executor thread from the `CommandLineInterface` # instance. def run(): # Send every second a redraw request. while not done[0]: time.sleep(1) cli.request_redraw() cli.eventloop.run_in_executor(run) def on_read_end(cli): done[0] = True app = Application( layout=Window(BufferControl(input_processors=[ClockPrompt()])), on_start=Callback(on_read_start), on_stop=Callback(on_read_end)) cli = CommandLineInterface(application=app, eventloop=eventloop) code_obj = cli.run() print('You said: %s' % code_obj.text) eventloop.close()
class UI(object): manager = KeyBindingManager() buffers = { DEFAULT_BUFFER: Buffer(is_multiline=True), 'MESSAGES': Buffer(is_multiline=True), } layout = VSplit([ Window( dont_extend_width=True, content=TokenListControl( get_tokens=lambda cli: [(Token, 'SlackTerm')], ), ), Window( width=D.exact(1), content=FillControl('|', token=Token.Line), ), HSplit([ Window(content=BufferControl(buffer_name='MESSAGES')), Window( height=D.exact(1), content=FillControl('_', token=Token.Line), ), Window( height=D.exact(3), content=BufferControl(buffer_name=DEFAULT_BUFFER), ), ]), ]) def __init__(self): application = Application( buffers=self.buffers, layout=self.layout, key_bindings_registry=self.manager.registry, mouse_support=True, use_alternate_screen=True, ) eventloop = create_asyncio_eventloop() self.cli = CommandLineInterface(application=application, eventloop=eventloop) sys.stdout = self.cli.stdout_proxy() async def start(self): while True: result = await self.cli.run_async() if not result: break @manager.registry.add_binding(Keys.ControlC, eager=True) def exit_(self): self.cli.set_return_value(None)
def main(): hy_repl = HyREPL() eventloop = create_eventloop() validator = HyValidator() history = FileHistory(expanduser("~/.pthy_history")) def src_is_multiline(): if app and app.buffer: text = app.buffer.document.text if '\n' in text: return True return False app = create_default_application("λ: ", validator=validator, multiline=Condition(src_is_multiline), lexer=HyLexer, style=HyStyle, history=history, completer=HyCompleter(hy_repl), display_completions_in_columns=True, extra_input_processors=[ ConditionalProcessor( processor=HighlightMatchingBracketProcessor(), filter=~IsDone()) ]) # Somewhat ugly trick to add a margin to the multiline input # without needing to define a custom layout app.layout.children[0].children[1].content.content.margin = ConditionalMargin( NumberredMargin(), filter=IsMultiline()) cli = CommandLineInterface(application=app, eventloop=eventloop) load_modified_bindings(app.key_bindings_registry) hy_repl.cli = cli try: while True: try: code_obj = cli.run() hy_repl.evaluate(code_obj.text) except KeyboardInterrupt: pass except EOFError: pass finally: eventloop.close()
async def connected(self): # prompt_toolkit internally checks if it's on windows during output rendering but # we need to force that we use Vt100_Output not Win32_Output from prompt_toolkit import renderer renderer.is_windows = lambda: False def get_size(): return self._size self._cli = CommandLineInterface( application=create_prompt_application(self._shell.prompt), eventloop=UnstoppableEventLoop(create_asyncio_eventloop(self._loop)), input=PatchedStdinInput(sys.stdin), output=Vt100_Output(self, get_size)) self._cb = self._cli.create_eventloop_callbacks() self._inputstream = InputStream(self._cb.feed_key) # Taken from prompt_toolkit telnet server # https://github.com/jonathanslenders/python-prompt-toolkit/blob/99fa7fae61c9b4ed9767ead3b4f9b1318cfa875d/prompt_toolkit/contrib/telnet/server.py#L165 self._cli._is_running = True if self._shell.welcome_message is not None: self.send(self._shell.welcome_message.encode()) self._cli._redraw()
def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) if self._ptcomp is None: compl = IPCompleter(shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout) kbmanager = KeyBindingManager.for_prompt() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend )(suspend_to_bg) self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings_registry=kbmanager.registry, history=self.shell.debugger_history, completer= self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens ) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
def __init__(self, *a, **kw): vi_mode = kw.pop('vi_mode', False) history_filename = kw.pop('history_filename', None) configure = kw.pop('configure', None) title = kw.pop('title', None) super(InteractiveShellEmbed, self).__init__(*a, **kw) def get_globals(): return self.user_ns self._eventloop = create_eventloop() ipython_input = IPythonInput( self, get_globals=get_globals, vi_mode=vi_mode, history_filename=history_filename) if title: ipython_input.terminal_title = title if configure: configure(ipython_input) self._cli = CommandLineInterface( application=ipython_input.create_application(), eventloop=self._eventloop)
def __init__(self, config_directory='~/.pyvim'): # Vi options. self.show_line_numbers = True self.highlight_search = True self.paste_mode = False self.show_ruler = True self.show_wildmenu = True self.expand_tab = True # Insect spaces instead of tab characters. self.tabstop = 4 # Number of spaces that a tab character represents. self.incsearch = True # Show matches while typing search string. self.ignore_case = False # Ignore case while searching. self.display_unprintable_characters = True # ':set list' self.enable_jedi = True # ':set jedi', for Python Jedi completion. self.scroll_offset = 0 # ':set scrolloff' # Ensure config directory exists. self.config_directory = os.path.abspath(os.path.expanduser(config_directory)) if not os.path.exists(self.config_directory): os.mkdir(self.config_directory) self._reporters_running_for_buffer_names = set() self.window_arrangement = WindowArrangement(self) self.message = None # Load styles. (Mapping from name to Style class.) self.styles = generate_built_in_styles() self.current_style = get_editor_style_by_name('default') # I/O backends. self.io_backends = [ DirectoryIO(), HttpIO(), GZipFileIO(), # Should come before FileIO. FileIO(), ] # Create eventloop. self.eventloop = create_eventloop() # Create key bindings manager self.key_bindings_manager = create_key_bindings(self) # Create layout and CommandLineInterface instance. self.editor_layout = EditorLayout( self, self.key_bindings_manager, self.window_arrangement) self.application = self._create_application() self.cli = CommandLineInterface( eventloop=self.eventloop, application=self.application) # Hide message when a key is pressed. def key_pressed(): self.message = None self.cli.input_processor.beforeKeyPress += key_pressed # Command line previewer. self.previewer = CommandPreviewer(self)
def init_prompt_toolkit_cli(self): if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): isp = self.input_splitter prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens()) prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens()) while isp.push_accepts_more(): line = cast_unicode_py2(input(prompt_text)) isp.push(line) prompt_text = prompt_continuation return isp.source_reset() self.prompt_for_code = prompt return # Set up keyboard shortcuts kbmanager = KeyBindingManager.for_prompt( enable_open_in_editor=self.extra_open_editor_shortcuts, ) register_ipython_shortcuts(kbmanager.registry, self) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) last_cell = cell self._style = self._make_style_from_name_or_cls(self.highlighting_style) self.style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) self._pt_app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(shell=self, patch_stdout=patch_stdout), enable_history_search=True, style=self.style, mouse_support=self.mouse_support, **self._layout_options() ) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface( self._pt_app, eventloop=self._eventloop, output=create_output(true_color=self.true_color))
def __init__(self): application = Application( buffers=self.buffers, layout=self.layout, key_bindings_registry=self.manager.registry, mouse_support=True, use_alternate_screen=True, ) eventloop = create_asyncio_eventloop() self.cli = CommandLineInterface(application=application, eventloop=eventloop) sys.stdout = self.cli.stdout_proxy()
class InteractiveShellEmbed(_InteractiveShellEmbed): """ Override the `InteractiveShellEmbed` from IPython, to replace the front-end with our input shell. :param configure: Callable for configuring the repl. """ def __init__(self, *a, **kw): vi_mode = kw.pop('vi_mode', False) history_filename = kw.pop('history_filename', None) configure = kw.pop('configure', None) title = kw.pop('title', None) super(InteractiveShellEmbed, self).__init__(*a, **kw) def get_globals(): return self.user_ns self._eventloop = create_eventloop() ipython_input = IPythonInput( self, get_globals=get_globals, vi_mode=vi_mode, history_filename=history_filename) if title: ipython_input.terminal_title = title if configure: configure(ipython_input) ipython_input.prompt_style = 'ipython' # Don't take from config. self._cli = CommandLineInterface( application=ipython_input.create_application(), eventloop=self._eventloop) def prompt_for_code(self): # IPython 5.0 calls `prompt_for_code` instead of `raw_input`. return self.raw_input(self) def raw_input(self, prompt=''): print('') try: string = self._cli.run(reset_current_buffer=True).text # In case of multiline input, make sure to append a newline to the input, # otherwise, IPython will ask again for more input in some cases. if '\n' in string: return string + '\n\n' else: return string except EOFError: self.ask_exit() return ''
def __init__(self): pdb.Pdb.__init__(self) # Cache for the grammar. self._grammar_cache = None # (current_pdb_commands, grammar) tuple. self._cli_history = FileHistory(os.path.expanduser('~/.ptpdb_history')) self.completer = None self.validator = None self.python_input = PythonInput( get_locals=lambda: self.curframe.f_locals, get_globals=lambda: self.curframe.f_globals, _completer=DynamicCompleter(lambda: self.completer), _validator=DynamicValidator(lambda: self.validator), _python_prompt_control=PdbLeftMargin(self._get_current_pdb_commands()), _extra_buffers={'source_code': Buffer()}, _extra_buffer_processors=[CompletionHint()], _extra_sidebars=[ HSplit([ FileLocationToolbar(weakref.ref(self)), ConditionalContainer( Window( BufferControl( buffer_name='source_code', lexer=PythonLexer, ), ), filter=~IsDone()), PdbShortcutsToolbar(weakref.ref(self)), ]), ], ) # Set UI styles. self.python_input.ui_styles = { 'ptpdb': get_ui_style(), } self.python_input.use_ui_colorscheme('ptpdb') # Set autocompletion style. (Multi-column works nicer.) self.python_input.completion_visualisation = CompletionVisualisation.MULTI_COLUMN self.python_input.complete_while_typing = False # Load additional key bindings. load_custom_pdb_key_bindings(self.python_input.key_bindings_registry) self.cli = CommandLineInterface( eventloop=create_eventloop(), application=self.python_input.create_application())
def run(): # We need to create an eventloop for this application. An eventloop is # basically a while-true loop that waits for user input, and when it # receives something (like a key press), it will send that to the # application. Usually, you want to use this `create_eventloop` shortcut, # which -- according to the environment (Windows/posix) -- returns # something that will work there. If you want to run your application # inside an "asyncio" environment, you'd have to pass another eventloop. eventloop = create_eventloop() try: # Create a `CommandLineInterface` instance. This is a wrapper around # `Application`, but includes all I/O: eventloops, terminal input and output. cli = CommandLineInterface(application=application, eventloop=eventloop) # Run the interface. (This runs the event loop until Ctrl-Q is pressed.) cli.run() finally: # Clean up. An eventloop creates a posix pipe. This is used internally # for scheduling callables, created in other threads into the main # eventloop. Calling `close` will close this pipe. eventloop.close()
def _feed_cli_with_input(text, editing_mode=EditingMode.EMACS): """ Create a CommandLineInterface, feed it with the given user input and return the CLI object. This returns a (result, CLI) tuple. """ # If the given text doesn't end with a newline, the interface won't finish. assert text.endswith('\n') loop = PosixEventLoop() try: inp = PipeInput() inp.send_text(text) cli = CommandLineInterface( application=Application(editing_mode=editing_mode), eventloop=loop, input=inp, output=DummyOutput()) result = cli.run() return result, cli finally: loop.close() inp.close()
def run_async(self): ''' Runs the user interface as an async task. ''' eventloop = create_asyncio_eventloop() self.cli = CommandLineInterface(application=self.application, eventloop=eventloop) self.cli.focus(COMMAND_BUFFER) try: while True: result = yield from self.cli.run_async() if result is None: print('Exiting...') return except (EOFError, KeyboardInterrupt): return finally: eventloop.close()
def set_application(self, app, callback=None): """ Set ``CommandLineInterface`` instance for this connection. (This can be replaced any time.) :param cli: CommandLineInterface instance. :param callback: Callable that takes the result of the CLI. """ assert isinstance(app, Application) assert callback is None or callable(callback) self.cli = CommandLineInterface( application=app, eventloop=self.eventloop, output=self.vt100_output) self.callback = callback # Create a parser, and parser callbacks. cb = self.cli.create_eventloop_callbacks() inputstream = InputStream(cb.feed_key) # Input decoder for stdin. (Required when working with multibyte # characters, like chinese input.) stdin_decoder_cls = getincrementaldecoder(self.encoding) stdin_decoder = [stdin_decoder_cls()] # nonlocal # Tell the CLI that it's running. We don't start it through the run() # call, but will still want _redraw() to work. self.cli._is_running = True def data_received(data): """ TelnetProtocolParser 'data_received' callback """ assert isinstance(data, binary_type) try: result = stdin_decoder[0].decode(data) inputstream.feed(result) except UnicodeDecodeError: stdin_decoder[0] = stdin_decoder_cls() return '' def size_received(rows, columns): """ TelnetProtocolParser 'size_received' callback """ self.size = Size(rows=rows, columns=columns) cb.terminal_size_changed() self.parser = TelnetProtocolParser(data_received, size_received)
class InteractiveShellEmbed(_InteractiveShellEmbed): """ Override the `InteractiveShellEmbed` from IPython, to replace the front-end with our input shell. :param configure: Callable for configuring the repl. """ def __init__(self, *a, **kw): vi_mode = kw.pop("vi_mode", False) history_filename = kw.pop("history_filename", None) configure = kw.pop("configure", None) title = kw.pop("title", None) super(InteractiveShellEmbed, self).__init__(*a, **kw) def get_globals(): return self.user_ns self._eventloop = create_eventloop() ipython_input = IPythonInput(self, get_globals=get_globals, vi_mode=vi_mode, history_filename=history_filename) if title: ipython_input.terminal_title = title if configure: configure(ipython_input) self._cli = CommandLineInterface(application=ipython_input.create_application(), eventloop=self._eventloop) def raw_input(self, prompt=""): print("") try: string = self._cli.run().text # In case of multiline input, make sure to append a newline to the input, # otherwise, IPython will ask again for more input in some cases. if "\n" in string: return string + "\n\n" else: return string except EOFError: self.ask_exit() return ""
def __init__(self, get_globals, get_locals=None): assert callable(get_globals) assert get_locals is None or callable(get_locals) self._chan = None def _globals(): data = get_globals() data.setdefault('print', self._print) return data repl = PythonRepl(get_globals=_globals, get_locals=get_locals or _globals) # Disable open-in-editor and system prompt. Because it would run and # display these commands on the server side, rather than in the SSH # client. repl.enable_open_in_editor = False repl.enable_system_bindings = False # PipInput object, for sending input in the CLI. # (This is something that we can use in the prompt_toolkit event loop, # but still write date in manually.) self._input_pipe = PipeInput() # Output object. Don't render to the real stdout, but write everything # in the SSH channel. class Stdout(object): def write(s, data): if self._chan is not None: self._chan.write(data.replace('\n', '\r\n')) def flush(s): pass # Create command line interface. self.cli = CommandLineInterface( application=repl.create_application(), eventloop=create_asyncio_eventloop(), input=self._input_pipe, output=Vt100_Output(Stdout(), self._get_size)) self._callbacks = self.cli.create_eventloop_callbacks()
def init_prompt_toolkit_cli(self): if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return # Set up keyboard shortcuts kbmanager = KeyBindingManager.for_prompt() register_ipython_shortcuts(kbmanager.registry, self) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) last_cell = cell self._style = self._make_style_from_name_or_cls(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._pt_app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(shell=self), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options() ) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface( self._pt_app, eventloop=self._eventloop, output=create_output(true_color=self.true_color))
def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) if self._ptcomp is None: compl = IPCompleter(shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout) kbmanager = KeyBindingManager.for_prompt() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend )(suspend_to_bg) if self.shell.display_completions == 'readlinelike': kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & ViInsertMode() | EmacsInsertMode() & ~cursor_in_leading_ws ))(display_completions_like_readline) multicolumn = (self.shell.display_completions == 'multicolumn') self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings_registry=kbmanager.registry, history=self.shell.debugger_history, completer= self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens, display_completions_in_columns=multicolumn, style=self.shell.style ) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] if self._ptcomp is None: compl = IPCompleter(shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl) self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), history=self.shell.debugger_history, completer= self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens ) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
def example_repl(self, text, example, start_index, continue_flag): """ REPL for interactive tutorials """ from prompt_toolkit.interface import CommandLineInterface if start_index: start_index = start_index + 1 cmd = ' '.join(text.split()[:start_index]) example_cli = CommandLineInterface( application=self.create_application( full_layout=False), eventloop=create_eventloop()) example_cli.buffers['example_line'].reset( initial_document=Document(u'{}\n'.format( add_new_lines(example))) ) while start_index < len(text.split()): if self.default_command: cmd = cmd.replace(self.default_command + ' ', '') example_cli.buffers[DEFAULT_BUFFER].reset( initial_document=Document( u'{}'.format(cmd), cursor_position=len(cmd))) example_cli.request_redraw() answer = example_cli.run() if not answer: return "", True answer = answer.text if answer.strip('\n') == cmd.strip('\n'): continue else: if len(answer.split()) > 1: start_index += 1 cmd += " " + answer.split()[-1] + " " +\ u' '.join(text.split()[start_index:start_index + 1]) example_cli.exit() del example_cli else: cmd = text return cmd, continue_flag
class ReplSSHServerSession(asyncssh.SSHServerSession): """ SSH server session that runs a Python REPL. :param get_globals: callable that returns the current globals. :param get_locals: (optional) callable that returns the current locals. """ def __init__(self, get_globals, get_locals=None): assert callable(get_globals) assert get_locals is None or callable(get_locals) self._chan = None def _globals(): data = get_globals() data.setdefault('print', self._print) return data repl = PythonRepl(get_globals=_globals, get_locals=get_locals or _globals) # Disable open-in-editor and system prompt. Because it would run and # display these commands on the server side, rather than in the SSH # client. repl.enable_open_in_editor = False repl.enable_system_bindings = False # PipInput object, for sending input in the CLI. # (This is something that we can use in the prompt_toolkit event loop, # but still write date in manually.) self._input_pipe = PipeInput() # Output object. Don't render to the real stdout, but write everything # in the SSH channel. class Stdout(object): def write(s, data): if self._chan is not None: self._chan.write(data.replace('\n', '\r\n')) def flush(s): pass # Create command line interface. self.cli = CommandLineInterface(application=repl.create_application(), eventloop=create_asyncio_eventloop(), input=self._input_pipe, output=Vt100_Output( Stdout(), self._get_size)) self._callbacks = self.cli.create_eventloop_callbacks() def _get_size(self): """ Callable that returns the current `Size`, required by Vt100_Output. """ if self._chan is None: return Size(rows=20, columns=79) else: width, height, pixwidth, pixheight = self._chan.get_terminal_size() return Size(rows=height, columns=width) def connection_made(self, chan): """ Client connected, run repl in coroutine. """ self._chan = chan # Run REPL interface. f = asyncio.ensure_future(self.cli.run_async()) # Close channel when done. def done(_): chan.close() self._chan = None f.add_done_callback(done) def shell_requested(self): return True def terminal_size_changed(self, width, height, pixwidth, pixheight): """ When the terminal size changes, report back to CLI. """ self._callbacks.terminal_size_changed() def data_received(self, data, datatype): """ When data is received, send to inputstream of the CLI and repaint. """ self._input_pipe.send(data) def _print(self, *data, **kw): """ _print(self, *data, sep=' ', end='\n', file=None) Alternative 'print' function that prints back into the SSH channel. """ # Pop keyword-only arguments. (We cannot use the syntax from the # signature. Otherwise, Python2 will give a syntax error message when # installing.) sep = kw.pop('sep', ' ') end = kw.pop('end', '\n') _ = kw.pop('file', None) assert not kw, 'Too many keyword-only arguments' data = sep.join(map(str, data)) self._chan.write(data + end)
class PtPdb(pdb.Pdb): def __init__(self): pdb.Pdb.__init__(self) # Cache for the grammar. self._grammar_cache = None # (current_pdb_commands, grammar) tuple. self.completer = None self.validator = None self.lexer = None self._source_code_window = Window(BufferControl( buffer_name='source_code', lexer=PygmentsLexer(PythonLexer), input_processors=[ HighlightSearchProcessor(preview_search=True), HighlightSelectionProcessor(), ], ), left_margins=[ SourceCodeMargin(self), NumberredMargin(), ], right_margins=[ScrollbarMargin()], scroll_offsets=ScrollOffsets( top=2, bottom=2), height=LayoutDimension(preferred=10)) # Callstack window. callstack = CallStack(weakref.ref(self)) self.callstack_focussed = False # When True, show cursor there, and allow navigation through it. self.callstack_selected_frame = 0 # Top frame. show_pdb_content_filter = ~IsDone() & Condition( lambda cli: not self.python_input.show_exit_confirmation) self.python_input = PythonInput( get_locals=lambda: self.curframe.f_locals, get_globals=lambda: self.curframe.f_globals, _completer=DynamicCompleter(lambda: self.completer), _validator=DynamicValidator(lambda: self.validator), _accept_action=self._create_accept_action(), _extra_buffers={'source_code': Buffer(read_only=True)}, _input_buffer_height=LayoutDimension(min=2, max=4), _lexer=PdbLexer(), _extra_buffer_processors=[ ConditionalProcessor(processor=CompletionHint(), filter=~IsDone()) ], _extra_layout_body=ConditionalContainer( HSplit([ VSplit([ HSplit([ SourceTitlebar(weakref.ref(self)), FloatContainer( content=self._source_code_window, floats=[ Float(right=0, bottom=0, content=BreakPointInfoToolbar( weakref.ref(self))) ]), ]), HSplit([ Window(width=LayoutDimension.exact(1), height=LayoutDimension.exact(1), content=FillControl( '\u252c', token=Token.Toolbar.Title)), Window(width=LayoutDimension.exact(1), content=FillControl('\u2502', token=Token.Separator)), ]), HSplit([ StackTitlebar(weakref.ref(self)), Window(callstack, scroll_offsets=ScrollOffsets(top=2, bottom=2), right_margins=[ScrollbarMargin()], height=LayoutDimension(preferred=10)), ]), ]), ]), filter=show_pdb_content_filter), _extra_toolbars=[ ConditionalContainer(PdbShortcutsToolbar(weakref.ref(self)), show_pdb_content_filter) ], history_filename=os.path.expanduser('~/.ptpdb_history'), ) # Override prompt style. self.python_input.all_prompt_styles['pdb'] = PdbPromptStyle( self._get_current_pdb_commands()) self.python_input.prompt_style = 'pdb' # Override exit message. self.python_input.exit_message = 'Do you want to quit BDB? This raises BdbQuit.' # Set UI styles. self.python_input.ui_styles = { 'ptpdb': get_ui_style(), } self.python_input.use_ui_colorscheme('ptpdb') # Set autocompletion style. (Multi-column works nicer.) self.python_input.completion_visualisation = CompletionVisualisation.MULTI_COLUMN # Load additional key bindings. load_custom_pdb_key_bindings(self, self.python_input.key_bindings_registry) self.cli = CommandLineInterface( eventloop=create_eventloop(), application=self.python_input.create_application()) def _create_accept_action(self): """ Create an AcceptAction for the input buffer that replaces shortcuts like 's' with the full command ('step') before returning it. """ def handler(cli, buffer): # Get first part. parts = buffer.text.strip().split(None, 1) if len(parts) == 0: first, rest = '', '' elif len(parts) == 1: first, rest = parts[0], '' else: first, rest = parts # Replace text in buffer and return it. buffer.document = Document( shortcuts.get(first, first) + ' ' + rest) cli.set_return_value(buffer.document) return AcceptAction(handler) def cmdloop(self, intro=None): """ Copy/Paste of pdb.Pdb.cmdloop. But using our own CommandLineInterface for reading input instead. """ self.preloop() if intro is not None: self.intro = intro if self.intro: self.stdout.write(str(self.intro) + "\n") stop = None while not stop: if self.cmdqueue: line = self.cmdqueue.pop(0) else: if self.use_rawinput: line = self._get_input() line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) self.postloop() def _get_current_pdb_commands(self): return (list(commands_with_help.keys()) + list(shortcuts.keys()) + list(self.aliases.keys())) def _create_grammar(self): """ Return the compiled grammar for this PDB shell. The grammar of PDB depends on the available list of PDB commands (which depends on the currently defined aliases.) Therefor we generate a new grammar when it changes, but cache it otherwise. (It's still expensive to compile.) """ pdb_commands = self._get_current_pdb_commands() if self._grammar_cache is None or self._grammar_cache[ 0] != pdb_commands: self._grammar_cache = [ pdb_commands, create_pdb_grammar(pdb_commands) ] return self._grammar_cache[1] def _get_input(self): """ Read PDB input. Return input text. """ # Reset multiline/paste mode every time. self.python_input.paste_mode = False self.python_input.currently_multiline = False # Set source code document. self._show_source_code(self.curframe.f_code.co_filename) self.cli.buffers[DEFAULT_BUFFER].document = Document('') # Select the current frame of the stack. for i, (frame, lineno) in enumerate(self.stack): if frame is self.curframe: self.callstack_selected_frame = i break # Set up a new completer and validator for the new grammar. g = self._create_grammar() self.completer = GrammarCompleter( g, completers={ 'enabled_breakpoint': BreakPointListCompleter(only_enabled=True), 'disabled_breakpoint': BreakPointListCompleter(only_disabled=True), 'alias_name': AliasCompleter(self), 'python_code': PythonCompleter(lambda: self.curframe.f_globals, lambda: self.curframe.f_locals), 'breakpoint': BreakPointListCompleter(), 'pdb_command': PdbCommandsCompleter(self), 'python_file': PythonFileCompleter(), 'python_function': PythonFunctionCompleter(self), }) self.validator = GrammarValidator(g, {'python_code': PythonValidator()}) # Make sure not to start in Vi navigation mode. self.python_input.key_bindings_manager.reset(self.cli) self.cli.buffers[DEFAULT_BUFFER].reset() def pre_run(): self._source_code_window.vertical_scroll = 100000 # source_code_doc.line_count try: return self.cli.run(reset_current_buffer=False, pre_run=pre_run).text except EOFError: # Turn Control-D key press into a 'quit' command. return 'quit' def _show_source_code(self, filename): """ Show the source code in the `source_code` buffer. """ source_code_doc = self._get_source_code_document(filename) self.cli.buffers['source_code']._set_text(source_code_doc.text + '\n') self.cli.buffers['source_code']._set_cursor_position( source_code_doc.cursor_position) def _get_source_code_document(self, filename): """ Return source code around current line as string. """ source_code = linecache.getlines(filename) if six.PY2: source_code = [l.decode('utf-8') for l in source_code] source_code = ''.join(source_code) document = Document(source_code) return Document( document.text, document.translate_row_col_to_index(row=self.curframe.f_lineno - 1, col=0)) # # Methods overriden from Pdb, in order to add highlighting. # def postcmd(self, stop, line): """ Override 'postcmd': (Insert whitespace.) """ print('') return pdb.Pdb.postcmd(self, stop, line) def preloop(self): print('') return pdb.Pdb.preloop(self) def do_interact(self, args): """ Interact: start interpreter. (Override the 'pdb' implementation. We call ptpython instead.) """ print('') ns = self.curframe.f_globals.copy() ns.update(self.curframe_locals) embed(globals=ns) def error(self, msg): """ Override default error handler from PDB. """ self.cli.print_tokens([(Token.Pdb.Error, ' %s \n' % msg)]) def print_stack_entry(self, frame_lineno, prompt_prefix=': '): """ Override `print_stack_entry` of Pdb, in order to add highlighting. """ frame, lineno = frame_lineno tokens = [] tokens.extend(format_stack_entry(self, frame, lineno)) tokens.append((Token, '\n')) self.cli.print_tokens(tokens) def do_list(self, arg): """ Override `Pdb.do_list`: Add highlighting. """ self.lastcmd = 'list' last = None if arg and arg != '.': try: if ',' in arg: first, last = arg.split(',') first = int(first.strip()) last = int(last.strip()) if last < first: # assume it's a count last = first + last else: first = int(arg.strip()) first = max(1, first - 5) except ValueError: self.error('Error in argument: %r' % arg) return elif self.lineno is None or arg == '.': first = max(1, self.curframe.f_lineno - 5) else: first = self.lineno + 1 if last is None: last = first + 10 filename = self.curframe.f_code.co_filename breaklist = self.get_file_breaks(filename) try: lines = linecache.getlines(filename, self.curframe.f_globals) self._print_lines_2(lines, first, last, breaklist, self.curframe) self.lineno = min(last, len(lines)) if len(lines) < last: self.message('[EOF]') except KeyboardInterrupt: pass do_l = do_list def _print_lines_2(self, lines, start, end, breaks=(), frame=None): """ Similar to `Pdb._print_lines`, except that this takes all the lines of the given file as input, it uses Pygments for the highlighting, it does slicing, and it prints everything in color. """ if frame: current_lineno = frame.f_lineno else: current_lineno = exc_lineno = -1 # Highlight everything. (Highlighting works much better from the # beginning of the file.) all_tokens = python_lexer.get_tokens(''.join(lines)) # Slice lines. lines = list(split_lines(all_tokens))[start - 1:end] # Add left margin. (Numbers + 'B' or '->'.) def add_margin(lineno, tokens): is_break = lineno in breaks is_current_line = lineno == current_lineno return get_line_prefix_tokens(is_break, is_current_line) \ + [(Token.LineNumber, str(lineno).rjust(3) + ' ')] \ + tokens + [(Token, '\n')] lines = [ add_margin(i + start, tokens) for i, tokens in enumerate(lines) ] for l in lines: self.cli.print_tokens(l) def message(self, msg): """ Print message to stdout. This function is present in Pdb for Python3, but not in Python2. """ print(msg, file=self.stdout)
class TerminalPdb(Pdb): def __init__(self, *args, **kwargs): Pdb.__init__(self, *args, **kwargs) self._ptcomp = None self.pt_init() def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] if self._ptcomp is None: compl = IPCompleter( shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl) self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), history=self.shell.debugger_history, completer=self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop) def cmdloop(self, intro=None): """Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. override the same methods from cmd.Cmd to provide prompt toolkit replacement. """ if not self.use_rawinput: raise ValueError('Sorry ipdb does not support use_rawinput=False') self.preloop() try: if intro is not None: self.intro = intro if self.intro: self.stdout.write(str(self.intro) + "\n") stop = None while not stop: if self.cmdqueue: line = self.cmdqueue.pop(0) else: self._ptcomp.ipy_completer.namespace = self.curframe_locals self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals try: line = self.pt_cli.run(reset_current_buffer=True).text except EOFError: line = 'EOF' line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) self.postloop() except Exception: raise
from prompt_toolkit.interface import CommandLineInterface from prompt_toolkit.application import Application from prompt_toolkit.shortcuts import create_eventloop from prompt_toolkit.enums import DEFAULT_BUFFER from prompt_toolkit.layout.containers import VSplit, Window from prompt_toolkit.layout.controls import BufferControl, FillControl, TokenListControl from prompt_toolkit.layout.dimension import LayoutDimension as D from pygments.token import Token layout = VSplit([ # One window that holds the BufferControl with the default buffer on the # left. Window(content=BufferControl(buffer_name=DEFAULT_BUFFER)), # A vertical line in the middle. We explicitely specify the width, to make # sure that the layout engine will not try to divide the whole width by # three for all these windows. The `FillControl` will simply fill the whole # window by repeating this character. Window(width=D.exact(1), content=FillControl('|', token=Token.Line)), # Display the text 'Hello world' on the right. Window(content=TokenListControl( get_tokens=lambda cli: [(Token, 'Hello world')])), ]) loop = create_eventloop() application = Application(layout=layout) cli = CommandLineInterface(application=application, eventloop=loop) cli.run() print('Exiting')
def setup_cli(self): """docstring for run""" self._cli = CommandLineInterface(application=self._application, eventloop=self._loop) self._cli.add_buffer(self._editor_buffer_name, self._editor_buffer) #, focus=True)
def create_interface(self): """ instantiates the interface """ from prompt_toolkit.interface import CommandLineInterface return CommandLineInterface(application=self.create_application(), eventloop=create_eventloop())
class ShellConnection(TelnetConnection): def __init__(self, reader, writer, shell, window_size_changed_callback, loop): super(ShellConnection, self).__init__(reader, writer, window_size_changed_callback) self._shell = shell self._loop = loop self._cli = None self._cb = None self._size = Size(rows=40, columns=79) self.encoding = 'utf-8' async def connected(self): # prompt_toolkit internally checks if it's on windows during output rendering but # we need to force that we use Vt100_Output not Win32_Output from prompt_toolkit import renderer renderer.is_windows = lambda: False def get_size(): return self._size self._cli = CommandLineInterface( application=create_prompt_application(self._shell.prompt), eventloop=UnstoppableEventLoop(create_asyncio_eventloop( self._loop)), input=PatchedStdinInput(sys.stdin), output=Vt100_Output(self, get_size)) self._cb = self._cli.create_eventloop_callbacks() self._inputstream = InputStream(self._cb.feed_key) # Taken from prompt_toolkit telnet server # https://github.com/jonathanslenders/python-prompt-toolkit/blob/99fa7fae61c9b4ed9767ead3b4f9b1318cfa875d/prompt_toolkit/contrib/telnet/server.py#L165 self._cli._is_running = True if self._shell.welcome_message is not None: self.send(self._shell.welcome_message.encode()) self._cli._redraw() async def disconnected(self): pass @asyncio.coroutine def window_size_changed(self, columns, rows): self._size = Size(rows=rows, columns=columns) self._cb.terminal_size_changed() if self._window_size_changed_callback: yield from self._window_size_changed_callback(columns, rows) async def feed(self, data): data = data.decode() self._inputstream.feed(data) self._cli._redraw() # Prompt toolkit has returned the command if self._cli.is_returning: try: returned_value = self._cli.return_value() except (EOFError, KeyboardInterrupt) as e: # don't close terminal, just keep it alive self.close() return command = returned_value.text res = await self._shell._parse_command(command) self.send(res.encode()) self.reset() def reset(self): """ Resets terminal screen""" self._cli.reset() self._cli.buffers[DEFAULT_BUFFER].reset() self._cli.renderer.request_absolute_cursor_position() self._cli._redraw() def write(self, data): """ Compat with CLI""" self.send(data) def flush(self): """ Compat with CLI""" pass
class TerminalPdb(Pdb): def __init__(self, *args, **kwargs): Pdb.__init__(self, *args, **kwargs) self._ptcomp = None self.pt_init() def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) if self._ptcomp is None: compl = IPCompleter( shell=self.shell, namespace={}, global_namespace={}, use_readline=False, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout) kbmanager = KeyBindingManager.for_prompt() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)(suspend_to_bg) if self.shell.display_completions == 'readlinelike': kbmanager.registry.add_binding( Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & ViInsertMode() | EmacsInsertMode() & ~cursor_in_leading_ws ))(display_completions_like_readline) multicolumn = (self.shell.display_completions == 'multicolumn') self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings_registry=kbmanager.registry, history=self.shell.debugger_history, completer=self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens, display_completions_in_columns=multicolumn, style=self.shell.style) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop) def cmdloop(self, intro=None): """Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. override the same methods from cmd.Cmd to provide prompt toolkit replacement. """ if not self.use_rawinput: raise ValueError('Sorry ipdb does not support use_rawinput=False') self.preloop() try: if intro is not None: self.intro = intro if self.intro: self.stdout.write(str(self.intro) + "\n") stop = None while not stop: if self.cmdqueue: line = self.cmdqueue.pop(0) else: self._ptcomp.ipy_completer.namespace = self.curframe_locals self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals try: line = self.pt_cli.run(reset_current_buffer=True).text except EOFError: line = 'EOF' line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) self.postloop() except Exception: raise
class TelnetConnection(object): """ Class that represents one Telnet connection. """ def __init__(self, conn, addr, application, server, encoding): assert isinstance(addr, tuple) # (addr, port) tuple assert isinstance(application, TelnetApplication) assert isinstance(server, TelnetServer) assert isinstance(encoding, text_type) # e.g. 'utf-8' self.conn = conn self.addr = addr self.application = application self.closed = False self.handling_command = True self.server = server self.encoding = encoding self.callback = None # Function that handles the CLI result. # Create "Output" object. self.size = Size(rows=40, columns=79) # Initialize. _initialize_telnet(conn) # Create output. def get_size(): return self.size self.stdout = _ConnectionStdout(conn, encoding=encoding) self.vt100_output = Vt100_Output(self.stdout, get_size, write_binary=False) # Create an eventloop (adaptor) for the CommandLineInterface. self.eventloop = _TelnetEventLoopInterface(server) # Set default CommandLineInterface. self.set_application(create_prompt_application()) # Call client_connected application.client_connected(self) # Draw for the first time. self.handling_command = False self.cli._redraw() def set_application(self, app, callback=None): """ Set ``CommandLineInterface`` instance for this connection. (This can be replaced any time.) :param cli: CommandLineInterface instance. :param callback: Callable that takes the result of the CLI. """ assert isinstance(app, Application) assert callback is None or callable(callback) self.cli = CommandLineInterface(application=app, eventloop=self.eventloop, output=self.vt100_output) self.callback = callback # Create a parser, and parser callbacks. cb = self.cli.create_eventloop_callbacks() inputstream = InputStream(cb.feed_key) # Input decoder for stdin. (Required when working with multibyte # characters, like chinese input.) stdin_decoder_cls = getincrementaldecoder(self.encoding) stdin_decoder = [stdin_decoder_cls()] # nonlocal # Tell the CLI that it's running. We don't start it through the run() # call, but will still want _redraw() to work. self.cli._is_running = True def data_received(data): """ TelnetProtocolParser 'data_received' callback """ assert isinstance(data, binary_type) try: result = stdin_decoder[0].decode(data) inputstream.feed(result) except UnicodeDecodeError: stdin_decoder[0] = stdin_decoder_cls() return '' def size_received(rows, columns): """ TelnetProtocolParser 'size_received' callback """ self.size = Size(rows=rows, columns=columns) cb.terminal_size_changed() self.parser = TelnetProtocolParser(data_received, size_received) def feed(self, data): """ Handler for incoming data. (Called by TelnetServer.) """ assert isinstance(data, binary_type) self.parser.feed(data) # Render again. self.cli._redraw() # When a return value has been set (enter was pressed), handle command. if self.cli.is_returning: try: return_value = self.cli.return_value() except (EOFError, KeyboardInterrupt) as e: # Control-D or Control-C was pressed. logger.info('%s, closing connection.', type(e).__name__) self.close() return # Handle CLI command self._handle_command(return_value) def _handle_command(self, command): """ Handle command. This will run in a separate thread, in order not to block the event loop. """ logger.info('Handle command %r', command) def in_executor(): self.handling_command = True try: if self.callback is not None: self.callback(self, command) finally: self.server.call_from_executor(done) def done(): self.handling_command = False # Reset state and draw again. (If the connection is still open -- # the application could have called TelnetConnection.close() if not self.closed: self.cli.reset() self.cli.buffers[DEFAULT_BUFFER].reset() self.cli.renderer.request_absolute_cursor_position() self.vt100_output.flush() self.cli._redraw() self.server.run_in_executor(in_executor) def erase_screen(self): """ Erase output screen. """ self.vt100_output.erase_screen() self.vt100_output.cursor_goto(0, 0) self.vt100_output.flush() def send(self, data): """ Send text to the client. """ assert isinstance(data, text_type) # When data is send back to the client, we should replace the line # endings. (We didn't allocate a real pseudo terminal, and the telnet # connection is raw, so we are responsible for inserting \r.) self.stdout.write(data.replace('\n', '\r\n')) self.stdout.flush() def close(self): """ Close the connection. """ self.application.client_leaving(self) self.conn.close() self.closed = True
class REPLApplication(ConsoleApplication): def __init__(self): self._script = None self._seqno = 0 self._ready = threading.Event() self._errors = 0 self._history = FileHistory( os.path.join(os.path.expanduser('~'), '.frida_history')) self._completer = FridaCompleter(self) self._cli = None self._last_change_id = 0 self._script_monitor = None self._monitored_file = None super(REPLApplication, self).__init__(self._process_input, self._on_stop) def _add_options(self, parser): parser.add_option("-l", "--load", help="load SCRIPT", metavar="SCRIPT", type='string', action='store', dest="user_script", default=None) parser.add_option("-e", "--eval", help="evaluate CODE", metavar="CODE", type='string', action='append', dest="eval_items", default=None) parser.add_option( "-q", help="quiet mode (no prompt) and quit after -l and -e", action='store_true', dest="quiet", default=False) parser.add_option( "--no-pause", help="automatically start main thread after startup", action='store_true', dest="no_pause", default=False) parser.add_option("-o", "--output", help="output to log file", dest="logfile", default=None) def _initialize(self, parser, options, args): self._user_script = options.user_script self._pending_eval = options.eval_items self._quiet = options.quiet self._no_pause = options.no_pause if options.logfile is not None: try: self._logfile = open(options.logfile, 'w') except Exception as e: self._update_status('Failed to open logfile "%s"' % options.logfile) self._exit(1) else: self._logfile = None def _log(self, level, text): ConsoleApplication._log(self, level, text) if self._logfile is not None: self._logfile.write(text + "\n") def _usage(self): return "usage: %prog [options] target" def _needs_target(self): return True def _start(self): self._prompt_string = self._create_prompt() try: self._load_script() except Exception as e: self._update_status( "Failed to load script: {error}".format(error=e)) self._exit(1) return if self._spawned_argv is not None: if self._no_pause: self._update_status( "Spawned `{command}`. Resuming main thread!".format( command=" ".join(self._spawned_argv))) self._do_magic("resume") else: self._update_status( "Spawned `{command}`. Use %resume to let the main thread start executing!" .format(command=" ".join(self._spawned_argv))) else: self._clear_status() self._ready.set() def _on_stop(self): def set_return(): raise EOFError() try: self._cli.eventloop.call_from_executor(set_return) except Exception: pass def _stop(self): self._unload_script() self._unmonitor_script() def _load_script(self): self._monitor_script() self._seqno += 1 script = self._session.create_script( name="repl%d" % self._seqno, source=self._create_repl_script()) script.set_log_handler(self._log) self._unload_script() self._script = script def on_message(message, data): self._reactor.schedule( lambda: self._process_message(message, data)) script.on('message', on_message) script.load() def _unload_script(self): if self._script is None: return try: self._script.unload() except: pass self._script = None def _monitor_script(self): if self._monitored_file == self._user_script: return self._unmonitor_script() if self._user_script is not None: monitor = frida.FileMonitor(self._user_script) monitor.on('change', self._on_change) monitor.enable() self._script_monitor = monitor self._monitored_file = self._user_script def _unmonitor_script(self): if self._script_monitor is None: return self._script_monitor.disable() self._script_monitor = None def _process_input(self, reactor): if not self._quiet: self._print_startup_message() while self._ready.wait(0.5) != True: if not reactor.is_running(): return while True: expression = "" line = "" while len(expression) == 0 or line.endswith("\\"): if not reactor.is_running(): return prompt = "[%s]" % self._prompt_string + "-> " if len( expression) == 0 else "... " pending_eval = self._pending_eval if pending_eval is not None: if len(pending_eval) > 0: expression = pending_eval.pop(0) if not self._quiet: self._print(prompt + expression) else: self._pending_eval = None else: if self._quiet: self._exit_status = 0 if self._errors == 0 else 1 return try: # We create the prompt manually instead of using get_input, # so we can use the cli in the _on_stop method eventloop = create_eventloop() self._cli = CommandLineInterface( application=create_prompt_application( prompt, history=self._history, completer=self._completer, lexer=JavascriptLexer), eventloop=eventloop, output=create_output()) try: line = None document = self._cli.run() if document: line = document.text finally: eventloop.close() except EOFError: # An extra newline after EOF to exit the REPL cleanly self._print("\nThank you for using Frida!") return except KeyboardInterrupt: line = "" continue if len(line.strip()) > 0: if len(expression) > 0: expression += "\n" expression += line.rstrip("\\") if expression.endswith("?"): try: self._print_help(expression) except JavaScriptError as e: error = e.error self._print(Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message']) except frida.InvalidOperationError: return elif expression.startswith("%"): self._do_magic(expression[1:].rstrip()) elif expression in ("exit", "quit", "q"): self._print("Thank you for using Frida!") return elif expression == "help": self._print("Help: #TODO :)") else: if not self._eval_and_print(expression): self._errors += 1 def _eval_and_print(self, expression): success = False try: (t, value) = self._evaluate(expression) if t in ('function', 'undefined', 'null'): output = t elif t == 'binary': output = hexdump(value).rstrip("\n") else: output = json.dumps(value, sort_keys=True, indent=4, separators=(",", ": ")) success = True except JavaScriptError as e: error = e.error output = Fore.RED + Style.BRIGHT + error[ 'name'] + Style.RESET_ALL + ": " + error['message'] except frida.InvalidOperationError: return success self._print(output) return success def _print_startup_message(self): self._print("""\ ____ / _ | Frida {version} - A world-class dynamic instrumentation framework | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/""".format( version=frida.__version__)) def _print_help(self, expression): # TODO: Figure out docstrings and implement here. This is real jankaty right now. help_text = "" if expression.endswith(".?"): expression = expression[:-2] + "?" obj_to_identify = [ x for x in expression.split(' ') if x.endswith("?") ][0][:-1] (obj_type, obj_value) = self._evaluate(obj_to_identify) if obj_type == "function": signature = self._evaluate("%s.toString()" % obj_to_identify)[1] clean_signature = signature.split("{")[0][:-1].split( 'function ')[-1] if "[native code]" in signature: help_text += "Type: Function (native)\n" else: help_text += "Type: Function\n" help_text += "Signature: %s\n" % clean_signature help_text += "Docstring: #TODO :)" elif obj_type == "object": help_text += "Type: Object\n" help_text += "Docstring: #TODO :)" elif obj_type == "boolean": help_text += "Type: Boolean\n" help_text += "Docstring: #TODO :)" elif obj_type == "string": help_text += "Type: Boolean\n" help_text += "Text: %s\n" % self._evaluate( "%s.toString()" % obj_to_identify)[1] help_text += "Docstring: #TODO :)" self._print(help_text) # Negative means at least abs(val) - 1 _magic_command_args = { 'resume': 0, 'load': 1, 'reload': 0, 'unload': 0, 'time': -2 # At least 1 arg } def _do_magic(self, statement): tokens = statement.split(" ") command = tokens[0] args = tokens[1:] required_args = self._magic_command_args.get(command) if required_args == None: self._print("Unknown command: {}".format(command)) self._print("Valid commands: {}".format(", ".join( self._magic_command_args.keys()))) return atleast_args = False if required_args < 0: atleast_args = True required_args = abs(required_args) - 1 if (not atleast_args and len(args) != required_args) or \ (atleast_args and len(args) < required_args): self._print( "{cmd} command expects {atleast}{n} argument{s}".format( cmd=command, atleast='atleast ' if atleast_args else '', n=required_args, s='' if required_args == 1 else ' ')) return if command == 'resume': self._reactor.schedule(lambda: self._resume()) elif command == 'load': old_user_script = self._user_script self._user_script = os.path.abspath(args[0]) if not self._reload(): self._user_script = old_user_script elif command == 'reload': self._reload() elif command == 'unload': self._user_script = None self._reload() elif command == 'time': self._eval_and_print(''' (function () { var _startTime = Date.now(); var _result = eval({expression}); var _endTime = Date.now(); console.log('Time: ' + (_endTime - _startTime).toLocaleString() + ' ms.'); return _result; })();'''.format(expression=json.dumps(' '.join(args)))) def _reload(self): completed = threading.Event() result = [None] def do_reload(): try: self._load_script() except Exception as e: result[0] = e completed.set() self._reactor.schedule(do_reload) completed.wait() if result[0] is None: return True else: self._print( "Failed to load script: {error}".format(error=result[0])) return False def _create_prompt(self): device_type = self._device.type type_name = self._target[0] if self._target[0] == 'pid' and self._target[1] == 0: target = 'System' else: target = self._target[1] if device_type in ('local', 'remote'): if self._target[0] == 'name': type_name = "ProcName" elif self._target[0] == 'pid': type_name = "PID" prompt_string = "%s::%s::%s" % (device_type.title(), type_name, target) else: prompt_string = "%s::%s::%s" % ("USB", self._device.name, target) return prompt_string def _evaluate(self, text): result = self._script.exports.evaluate(text) if is_byte_array(result): return ('binary', result) elif isinstance(result, dict): return ('binary', bytes()) elif result[0] == 'error': raise JavaScriptError(result[1]) else: return result def _process_message(self, message, data): message_type = message['type'] if message_type == 'error': text = message.get('stack', message['description']) self._log('error', text) self._errors += 1 else: self._print("message:", message, "data:", data) def _on_change(self, changed_file, other_file, event_type): if event_type == 'changes-done-hint': return self._last_change_id += 1 change_id = self._last_change_id self._reactor.schedule(lambda: self._process_change(change_id), delay=0.05) def _process_change(self, change_id): if change_id != self._last_change_id: return try: self._load_script() except Exception as e: self._print("Failed to load script: {error}".format(error=e)) def _create_repl_script(self): user_script = "" if self._user_script is not None: with codecs.open(self._user_script, 'rb', 'utf-8') as f: user_script = f.read().rstrip( "\r\n") + "\n\n// Frida REPL script:\n" return user_script + """\
def _process_input(self, reactor): if not self._quiet: self._print_startup_message() while self._ready.wait(0.5) != True: if not reactor.is_running(): return while True: expression = "" line = "" while len(expression) == 0 or line.endswith("\\"): if not reactor.is_running(): return prompt = "[%s]" % self._prompt_string + "-> " if len( expression) == 0 else "... " pending_eval = self._pending_eval if pending_eval is not None: if len(pending_eval) > 0: expression = pending_eval.pop(0) if not self._quiet: self._print(prompt + expression) else: self._pending_eval = None else: if self._quiet: self._exit_status = 0 if self._errors == 0 else 1 return try: # We create the prompt manually instead of using get_input, # so we can use the cli in the _on_stop method eventloop = create_eventloop() self._cli = CommandLineInterface( application=create_prompt_application( prompt, history=self._history, completer=self._completer, lexer=JavascriptLexer), eventloop=eventloop, output=create_output()) try: line = None document = self._cli.run() if document: line = document.text finally: eventloop.close() except EOFError: # An extra newline after EOF to exit the REPL cleanly self._print("\nThank you for using Frida!") return except KeyboardInterrupt: line = "" continue if len(line.strip()) > 0: if len(expression) > 0: expression += "\n" expression += line.rstrip("\\") if expression.endswith("?"): try: self._print_help(expression) except JavaScriptError as e: error = e.error self._print(Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message']) except frida.InvalidOperationError: return elif expression.startswith("%"): self._do_magic(expression[1:].rstrip()) elif expression in ("exit", "quit", "q"): self._print("Thank you for using Frida!") return elif expression == "help": self._print("Help: #TODO :)") else: if not self._eval_and_print(expression): self._errors += 1
def __init__(self): pdb.Pdb.__init__(self) # Cache for the grammar. self._grammar_cache = None # (current_pdb_commands, grammar) tuple. self.completer = None self.validator = None self.lexer = None self._source_code_window = Window(BufferControl( buffer_name='source_code', lexer=PygmentsLexer(PythonLexer), input_processors=[ HighlightSearchProcessor(preview_search=True), HighlightSelectionProcessor(), ], ), left_margins=[ SourceCodeMargin(self), NumberredMargin(), ], right_margins=[ScrollbarMargin()], scroll_offsets=ScrollOffsets( top=2, bottom=2), height=LayoutDimension(preferred=10)) # Callstack window. callstack = CallStack(weakref.ref(self)) self.callstack_focussed = False # When True, show cursor there, and allow navigation through it. self.callstack_selected_frame = 0 # Top frame. show_pdb_content_filter = ~IsDone() & Condition( lambda cli: not self.python_input.show_exit_confirmation) self.python_input = PythonInput( get_locals=lambda: self.curframe.f_locals, get_globals=lambda: self.curframe.f_globals, _completer=DynamicCompleter(lambda: self.completer), _validator=DynamicValidator(lambda: self.validator), _accept_action=self._create_accept_action(), _extra_buffers={'source_code': Buffer(read_only=True)}, _input_buffer_height=LayoutDimension(min=2, max=4), _lexer=PdbLexer(), _extra_buffer_processors=[ ConditionalProcessor(processor=CompletionHint(), filter=~IsDone()) ], _extra_layout_body=ConditionalContainer( HSplit([ VSplit([ HSplit([ SourceTitlebar(weakref.ref(self)), FloatContainer( content=self._source_code_window, floats=[ Float(right=0, bottom=0, content=BreakPointInfoToolbar( weakref.ref(self))) ]), ]), HSplit([ Window(width=LayoutDimension.exact(1), height=LayoutDimension.exact(1), content=FillControl( '\u252c', token=Token.Toolbar.Title)), Window(width=LayoutDimension.exact(1), content=FillControl('\u2502', token=Token.Separator)), ]), HSplit([ StackTitlebar(weakref.ref(self)), Window(callstack, scroll_offsets=ScrollOffsets(top=2, bottom=2), right_margins=[ScrollbarMargin()], height=LayoutDimension(preferred=10)), ]), ]), ]), filter=show_pdb_content_filter), _extra_toolbars=[ ConditionalContainer(PdbShortcutsToolbar(weakref.ref(self)), show_pdb_content_filter) ], history_filename=os.path.expanduser('~/.ptpdb_history'), ) # Override prompt style. self.python_input.all_prompt_styles['pdb'] = PdbPromptStyle( self._get_current_pdb_commands()) self.python_input.prompt_style = 'pdb' # Override exit message. self.python_input.exit_message = 'Do you want to quit BDB? This raises BdbQuit.' # Set UI styles. self.python_input.ui_styles = { 'ptpdb': get_ui_style(), } self.python_input.use_ui_colorscheme('ptpdb') # Set autocompletion style. (Multi-column works nicer.) self.python_input.completion_visualisation = CompletionVisualisation.MULTI_COLUMN # Load additional key bindings. load_custom_pdb_key_bindings(self, self.python_input.key_bindings_registry) self.cli = CommandLineInterface( eventloop=create_eventloop(), application=self.python_input.create_application())
class Editor(object): """ The main class. Containing the whole editor. """ def __init__(self, config_directory='~/.pyvim'): # Vi options. self.show_line_numbers = True self.highlight_search = True self.paste_mode = False self.show_ruler = True self.show_wildmenu = True self.expand_tab = True # Insect spaces instead of tab characters. self.tabstop = 4 # Number of spaces that a tab character represents. self.incsearch = True # Show matches while typing search string. self.ignore_case = False # Ignore case while searching. self.enable_mouse_support = True self.display_unprintable_characters = True # ':set list' self.enable_jedi = True # ':set jedi', for Python Jedi completion. self.scroll_offset = 0 # ':set scrolloff' self.relative_number = False # ':set relativenumber' self.wrap_lines = True # ':set wrap' # Ensure config directory exists. self.config_directory = os.path.abspath( os.path.expanduser(config_directory)) if not os.path.exists(self.config_directory): os.mkdir(self.config_directory) self._reporters_running_for_buffer_names = set() self.window_arrangement = WindowArrangement(self) self.message = None # Load styles. (Mapping from name to Style class.) self.styles = generate_built_in_styles() self.current_style = get_editor_style_by_name('default') # I/O backends. self.io_backends = [ DirectoryIO(), HttpIO(), GZipFileIO(), # Should come before FileIO. FileIO(), ] # Create eventloop. self.eventloop = create_eventloop() # Create key bindings manager self.key_bindings_manager = create_key_bindings(self) # Create layout and CommandLineInterface instance. self.editor_layout = EditorLayout(self, self.key_bindings_manager, self.window_arrangement) self.application = self._create_application() self.cli = CommandLineInterface(eventloop=self.eventloop, application=self.application) # Hide message when a key is pressed. def key_pressed(): self.message = None self.cli.input_processor.beforeKeyPress += key_pressed # Command line previewer. self.previewer = CommandPreviewer(self) def load_initial_files(self, locations, in_tab_pages=False, hsplit=False, vsplit=False): """ Load a list of files. """ assert in_tab_pages + hsplit + vsplit <= 1 # Max one of these options. # When no files were given, open at least one empty buffer. locations2 = locations or [None] # First file self.window_arrangement.open_buffer(locations2[0]) for f in locations2[1:]: if in_tab_pages: self.window_arrangement.create_tab(f) elif hsplit: self.window_arrangement.hsplit(location=f) elif vsplit: self.window_arrangement.vsplit(location=f) else: self.window_arrangement.open_buffer(f) self.window_arrangement.active_tab_index = 0 if locations and len(locations) > 1: self.show_message('%i files loaded.' % len(locations)) def _create_application(self): """ Create CommandLineInterface instance. """ # Create Vi command buffer. def handle_action(cli, buffer): ' When enter is pressed in the Vi command line. ' text = buffer.text # Remember: leave_command_mode resets the buffer. # First leave command mode. We want to make sure that the working # pane is focussed again before executing the command handlers. self.leave_command_mode(append_to_history=True) # Execute command. handle_command(self, text) # Create history and search buffers. commands_history = FileHistory( os.path.join(self.config_directory, 'commands_history')) command_buffer = Buffer( accept_action=AcceptAction(handler=handle_action), enable_history_search=Always(), completer=create_command_completer(self), history=commands_history) search_buffer_history = FileHistory( os.path.join(self.config_directory, 'search_history')) search_buffer = Buffer(history=search_buffer_history, enable_history_search=Always(), accept_action=AcceptAction.IGNORE) # Create app. # Create CLI. application = Application( layout=self.editor_layout.layout, key_bindings_registry=self.key_bindings_manager.registry, get_title=lambda: get_terminal_title(self), buffers={ COMMAND_BUFFER: command_buffer, SEARCH_BUFFER: search_buffer, }, style=DynamicStyle(lambda: PygmentsStyle(self.current_style)), paste_mode=Condition(lambda cli: self.paste_mode), ignore_case=Condition(lambda cli: self.ignore_case), mouse_support=Condition(lambda cli: self.enable_mouse_support), use_alternate_screen=True, on_buffer_changed=Callback(self._current_buffer_changed)) # Handle command line previews. # (e.g. when typing ':colorscheme blue', it should already show the # preview before pressing enter.) def preview(): if self.cli.current_buffer == command_buffer: self.previewer.preview(command_buffer.text) command_buffer.on_text_changed += preview return application @property def current_editor_buffer(self): """ Return the `EditorBuffer` that is currently active. """ # For each buffer name on the focus stack. for current_buffer_name in [ self.cli.current_buffer_name, self.cli.focus_stack.previous ]: if current_buffer_name is not None: # Find/return the EditorBuffer with this name. for b in self.window_arrangement.editor_buffers: if b.buffer_name == current_buffer_name: return b @property def add_key_binding(self): """ Shortcut for adding new key bindings. (Mostly useful for a pyvimrc file, that receives this Editor instance as input.) """ return self.key_bindings_manager.registry.add_binding def show_message(self, message): """ Set a warning message. The layout will render it as a "pop-up" at the bottom. """ self.message = message def use_colorscheme(self, name='default'): """ Apply new colorscheme. (By name.) """ try: self.current_style = get_editor_style_by_name(name) except pygments.util.ClassNotFound: pass def sync_with_prompt_toolkit(self): """ Update the prompt-toolkit Layout and FocusStack. """ # After executing a command, make sure that the layout of # prompt-toolkit matches our WindowArrangement. self.editor_layout.update() # Make sure that the focus stack of prompt-toolkit has the current # page. self.cli.focus_stack._stack = [ self.window_arrangement.active_editor_buffer.buffer_name ] def _current_buffer_changed(self, cli): """ Current buffer changed. """ name = self.cli.current_buffer_name eb = self.window_arrangement.get_editor_buffer_for_buffer_name(name) if eb is not None: # Run reporter. self.run_reporter_for_editor_buffer(eb) def run_reporter_for_editor_buffer(self, editor_buffer): """ Run reporter on input. (Asynchronously.) """ assert isinstance(editor_buffer, EditorBuffer) eb = editor_buffer name = eb.buffer_name if name not in self._reporters_running_for_buffer_names: text = eb.buffer.text self._reporters_running_for_buffer_names.add(name) # Don't run reporter when we don't have a location. (We need to # know the filetype, actually.) if eb.location is None: return # Better not to access the document in an executor. document = eb.buffer.document def in_executor(): # Call reporter report_errors = report(eb.location, document) def ready(): self._reporters_running_for_buffer_names.remove(name) # If the text has not been changed yet in the meantime, set # reporter errors. (We were running in another thread.) if text == eb.buffer.text: eb.report_errors = report_errors self.cli._redraw() else: # Restart reporter when the text was changed. self._current_buffer_changed(self.cli) self.cli.eventloop.call_from_executor(ready) self.cli.eventloop.run_in_executor(in_executor) def show_help(self): """ Show help in new window. """ self.window_arrangement.hsplit(text=HELP_TEXT) self.sync_with_prompt_toolkit() # Show new window. def run(self): """ Run the event loop for the interface. This starts the interaction. """ # Make sure everything is in sync, before starting. self.sync_with_prompt_toolkit() # Run eventloop of prompt_toolkit. self.cli.run(reset_current_buffer=False) def enter_command_mode(self): """ Go into command mode. """ self.cli.focus_stack.push(COMMAND_BUFFER) self.key_bindings_manager.vi_state.input_mode = InputMode.INSERT self.previewer.save() def leave_command_mode(self, append_to_history=False): """ Leave command mode. Focus document window again. """ self.previewer.restore() self.cli.focus_stack.pop() self.key_bindings_manager.vi_state.input_mode = InputMode.NAVIGATION self.cli.buffers[COMMAND_BUFFER].reset( append_to_history=append_to_history)
def __init__(self, config_directory='~/.pyvim'): # Vi options. self.show_line_numbers = True self.highlight_search = True self.paste_mode = False self.show_ruler = True self.show_wildmenu = True self.expand_tab = True # Insect spaces instead of tab characters. self.tabstop = 4 # Number of spaces that a tab character represents. self.incsearch = True # Show matches while typing search string. self.ignore_case = False # Ignore case while searching. self.enable_mouse_support = True self.display_unprintable_characters = True # ':set list' self.enable_jedi = True # ':set jedi', for Python Jedi completion. self.scroll_offset = 0 # ':set scrolloff' self.relative_number = False # ':set relativenumber' self.wrap_lines = True # ':set wrap' # Ensure config directory exists. self.config_directory = os.path.abspath( os.path.expanduser(config_directory)) if not os.path.exists(self.config_directory): os.mkdir(self.config_directory) self._reporters_running_for_buffer_names = set() self.window_arrangement = WindowArrangement(self) self.message = None # Load styles. (Mapping from name to Style class.) self.styles = generate_built_in_styles() self.current_style = get_editor_style_by_name('default') # I/O backends. self.io_backends = [ DirectoryIO(), HttpIO(), GZipFileIO(), # Should come before FileIO. FileIO(), ] # Create eventloop. self.eventloop = create_eventloop() # Create key bindings manager self.key_bindings_manager = create_key_bindings(self) # Create layout and CommandLineInterface instance. self.editor_layout = EditorLayout(self, self.key_bindings_manager, self.window_arrangement) self.application = self._create_application() self.cli = CommandLineInterface(eventloop=self.eventloop, application=self.application) # Hide message when a key is pressed. def key_pressed(): self.message = None self.cli.input_processor.beforeKeyPress += key_pressed # Command line previewer. self.previewer = CommandPreviewer(self)
def _create_cli(self): """Creates a new cli""" history = FileHistory('.history') def toolbar_handler(_): """Returns bottom menu items. Args: * _: An instance of prompt_toolkit's Cli (not used). Returns: A list of Token.Toolbar. """ return [(Token.Toolbar, ' [F9] Docs '), (Token.Toolbar, ' [F10] Exit ')] def url_handler(_): if self.service.last_request is not None: method = ' ' + self.service.last_request['method'] url = ' ' + self.service.last_request['url'] headers = '' if self.service.last_request['headers'] is not None: headers = ' ' + '; '.join([ key + ': ' + value for key, value in self.service.last_request['headers'].items() ]) return [(Token.Method, method), (Token.Url, url), (Token.Headers, headers)] return [(Token.Method, ''), (Token.Url, ''), (Token.Headers, '')] layout = self._create_layout(message='atomix> ', reserve_space_for_menu=8, lexer=CommandLexer, get_bottom_toolbar_tokens=toolbar_handler, get_url_tokens=url_handler) buffer = Buffer(history=history, auto_suggest=CommandAutoSuggest(self.commands), enable_history_search=True, completer=CommandCompleter(self.commands), complete_while_typing=Always(), accept_action=AcceptAction.RETURN_DOCUMENT) key_manager = KeyBindingManager(enable_search=True, enable_abort_and_exit_bindings=True, enable_system_bindings=True, enable_auto_suggest_bindings=True) @key_manager.registry.add_binding(Keys.F9) def handle_f9(_): """Inputs the "docs" command when the `F9` key is pressed. Args: * _: An instance of prompt_toolkit's Event (not used). Returns: None. """ webbrowser.open('http://atomix.io') @key_manager.registry.add_binding(Keys.F10) def handle_f10(_): """Quits when the `F10` key is pressed.""" raise EOFError @key_manager.registry.add_binding(Keys.ControlSpace) def handle_ctrl_space(event): """Initializes autocompletion at the cursor. If the autocompletion menu is not showing, display it with the appropriate completions for the context. If the menu is showing, select the next completion. Args: * event: An instance of prompt_toolkit's Event. Returns: None. """ b = event.cli.current_buffer if b.complete_state: b.complete_next() else: event.cli.start_completion(select_first=False) application = Application(mouse_support=False, style=get_style(self.theme), layout=layout, buffer=buffer, key_bindings_registry=key_manager.registry, on_exit=AbortAction.RAISE_EXCEPTION, on_abort=AbortAction.RETRY, ignore_case=True) eventloop = create_eventloop() return CommandLineInterface(application=application, eventloop=eventloop)
class TerminalInteractiveShell(InteractiveShell): space_for_menu = Integer( 6, help='Number of line at the bottom of the screen ' 'to reserve for the completion menu').tag(config=True) def _space_for_menu_changed(self, old, new): self._update_layout() pt_cli = None debugger_history = None _pt_app = None simple_prompt = Bool( _use_simple_prompt, help= """Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: IPython own testing machinery, and emacs inferior-shell integration through elpy. This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` environment variable is set, or the current terminal is not a tty. """).tag(config=True) @property def debugger_cls(self): return Pdb if self.simple_prompt else TerminalPdb confirm_exit = Bool( True, help=""" Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a direct exit without any confirmation.""", ).tag(config=True) editing_mode = Unicode( 'emacs', help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", ).tag(config=True) mouse_support = Bool( False, help="Enable mouse support in the prompt").tag(config=True) highlighting_style = Union( [Unicode('legacy'), Type(klass=Style)], help="""The name or class of a Pygments style to use for syntax highlighting: \n %s""" % ', '.join(get_all_styles())).tag(config=True) @observe('highlighting_style') @observe('colors') def _highlighting_style_changed(self, change): self.refresh_style() def refresh_style(self): self._style = self._make_style_from_name_or_cls( self.highlighting_style) highlighting_style_overrides = Dict( help="Override highlighting format for specific tokens").tag( config=True) true_color = Bool( False, help=("Use 24bit colors instead of 256 colors in prompt highlighting. " "If your terminal supports true color, the following command " "should print 'TRUECOLOR' in orange: " "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")).tag( config=True) editor = Unicode( get_default_editor(), help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ).tag(config=True) prompts_class = Type( Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag( config=True) prompts = Instance(Prompts) @default('prompts') def _prompts_default(self): return self.prompts_class(self) @observe('prompts') def _(self, change): self._update_layout() @default('displayhook_class') def _displayhook_class_default(self): return RichPromptDisplayHook term_title = Bool( True, help="Automatically set the terminal title").tag(config=True) display_completions = Enum( ('column', 'multicolumn', 'readlinelike'), help= ("Options for displaying tab completions, 'column', 'multicolumn', and " "'readlinelike'. These options are for `prompt_toolkit`, see " "`prompt_toolkit` documentation for more information."), default_value='multicolumn').tag(config=True) highlight_matching_brackets = Bool( True, help="Highlight matching brackets .", ).tag(config=True) @observe('term_title') def init_term_title(self, change=None): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def init_display_formatter(self): super(TerminalInteractiveShell, self).init_display_formatter() # terminal only supports plain text self.display_formatter.active_types = ['text/plain'] # disable `_ipython_display_` self.display_formatter.ipython_display_formatter.enabled = False def init_prompt_toolkit_cli(self): if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return # Set up keyboard shortcuts kbmanager = KeyBindingManager.for_prompt() register_ipython_shortcuts(kbmanager.registry, self) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) last_cell = cell self._style = self._make_style_from_name_or_cls( self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) self._pt_app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(shell=self, patch_stdout=patch_stdout), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface( self._pt_app, eventloop=self._eventloop, output=create_output(true_color=self.true_color)) def _make_style_from_name_or_cls(self, name_or_cls): """ Small wrapper that make an IPython compatible style from a style name We need that to add style for prompt ... etc. """ style_overrides = {} if name_or_cls == 'legacy': legacy = self.colors.lower() if legacy == 'linux': style_cls = get_style_by_name('monokai') style_overrides = _style_overrides_linux elif legacy == 'lightbg': style_overrides = _style_overrides_light_bg style_cls = get_style_by_name('pastie') elif legacy == 'neutral': # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_cls = get_style_by_name('default') style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', }) # Hack: Due to limited color support on the Windows console # the prompt colors will be wrong without this if os.name == 'nt': style_overrides.update({ Token.Prompt: '#ansidarkgreen', Token.PromptNum: '#ansigreen bold', Token.OutPrompt: '#ansidarkred', Token.OutPromptNum: '#ansired bold', }) elif legacy == 'nocolor': style_cls = _NoStyle style_overrides = {} else: raise ValueError('Got unknown colors: ', legacy) else: if isinstance(name_or_cls, str): style_cls = get_style_by_name(name_or_cls) else: style_cls = name_or_cls style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', } style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) return style def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer': IPythonPTLexer(), 'reserve_space_for_menu': self.space_for_menu, 'get_prompt_tokens': self.prompts.in_prompt_tokens, 'get_continuation_tokens': self.prompts.continuation_prompt_tokens, 'multiline': True, 'display_completions_in_columns': (self.display_completions == 'multicolumn'), # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'extra_input_processors': [ ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: self.highlight_matching_brackets)) ], } def _update_layout(self): """ Ask for a re computation of the application layout, if for example , some configuration options have changed. """ if self._pt_app: self._pt_app.layout = create_prompt_layout( **self._layout_options()) def prompt_for_code(self): document = self.pt_cli.run(pre_run=self.pre_prompt, reset_current_buffer=True) return document.text def enable_win_unicode_console(self): if sys.version_info >= (3, 6): # Since PEP 528, Python uses the unicode APIs for the Windows # console by default, so WUC shouldn't be needed. return import win_unicode_console win_unicode_console.enable() def init_io(self): if sys.platform not in {'win32', 'cli'}: return self.enable_win_unicode_console() import colorama colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 # io.std* are deprecated, but don't show our own deprecation warnings # during initialization of the deprecated API. with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True self.debugger_history = InMemoryHistory() def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: # We can't set the buffer here, because it will be reset just after # this. Adding a callable to pre_run_callables does what we need # after the buffer is reset. s = cast_unicode_py2(self.rl_next_input) def set_doc(): self.pt_cli.application.buffer.document = Document(s) if hasattr(self.pt_cli, 'pre_run_callables'): self.pt_cli.pre_run_callables.append(set_doc) else: # Older version of prompt_toolkit; it's OK to set the document # directly here. set_doc() self.rl_next_input = None def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED): if display_banner is not DISPLAY_BANNER_DEPRECATED: warn( 'interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) self.keep_running = True while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if (not self.confirm_exit) \ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. if display_banner is not DISPLAY_BANNER_DEPRECATED: warn( 'mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) while True: try: self.interact() break except KeyboardInterrupt as e: print("\n%s escaped interact()\n" % type(e).__name__) finally: # An interrupt during the eventloop will mess up the # internal state of the prompt_toolkit library. # Stopping the eventloop fixes this, see # https://github.com/ipython/ipython/pull/9867 if hasattr(self, '_eventloop'): self._eventloop.stop() _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) active_eventloop = None def enable_gui(self, gui=None): if gui: self.active_eventloop, self._inputhook =\ get_inputhook_name_and_func(gui) else: self.active_eventloop = self._inputhook = None # Run !system commands directly, not through pipes, so terminal programs # work correctly. system = InteractiveShell.system_raw def auto_rewrite_input(self, cmd): """Overridden from the parent class to use fancy rewriting prompt""" if not self.show_rewritten_input: return tokens = self.prompts.rewrite_prompt_tokens() if self.pt_cli: self.pt_cli.print_tokens(tokens) print(cmd) else: prompt = ''.join(s for t, s in tokens) print(prompt, cmd, sep='') _prompts_before = None def switch_doctest_mode(self, mode): """Switch prompts to classic for %doctest_mode""" if mode: self._prompts_before = self.prompts self.prompts = ClassicPrompts(self) elif self._prompts_before: self.prompts = self._prompts_before self._prompts_before = None self._update_layout()
def init_prompt_toolkit_cli(self): self._app = None if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): event.current_buffer.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws)) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
class REPLApplication(ConsoleApplication): def __init__(self): self._script = None self._seqno = 0 self._ready = threading.Event() self._stopping = threading.Event() self._errors = 0 config_dir = self._get_or_create_config_dir() self._history = FileHistory(os.path.join(config_dir, 'history')) self._completer = FridaCompleter(self) self._cli = None self._last_change_id = 0 self._script_monitor = None self._monitored_file = None super(REPLApplication, self).__init__(self._process_input, self._on_stop) def _add_options(self, parser): parser.add_option("-l", "--load", help="load SCRIPT", metavar="SCRIPT", type='string', action='store', dest="user_script", default=None) parser.add_option("-c", "--codeshare", help="load CODESHARE_URI", metavar="CODESHARE_URI", type='string', action='store', dest="codeshare_uri", default=None) parser.add_option("-e", "--eval", help="evaluate CODE", metavar="CODE", type='string', action='append', dest="eval_items", default=None) parser.add_option("-q", help="quiet mode (no prompt) and quit after -l and -e", action='store_true', dest="quiet", default=False) parser.add_option("--no-pause", help="automatically start main thread after startup", action='store_true', dest="no_pause", default=False) parser.add_option("-o", "--output", help="output to log file", dest="logfile", default=None) def _initialize(self, parser, options, args): self._user_script = options.user_script self._codeshare_uri = options.codeshare_uri self._codeshare_script = None self._pending_eval = options.eval_items self._quiet = options.quiet self._no_pause = options.no_pause if options.logfile is not None: try: self._logfile = open(options.logfile, 'w') except Exception as e: self._update_status('Failed to open logfile "%s"' % options.logfile) self._exit(1) else: self._logfile = None def _log(self, level, text): ConsoleApplication._log(self, level, text) if self._logfile is not None: self._logfile.write(text + "\n") def _usage(self): return "usage: %prog [options] target" def _needs_target(self): return True def _start(self): self._prompt_string = self._create_prompt() if self._codeshare_uri is not None: self._codeshare_script = self._load_codeshare_script(self._codeshare_uri) if self._codeshare_script is None: self._print("Exiting!") self._exit(1) return try: self._load_script() except Exception as e: self._update_status("Failed to load script: {error}".format(error=e)) self._exit(1) return if self._spawned_argv is not None: if self._no_pause: self._update_status("Spawned `{command}`. Resuming main thread!".format(command=" ".join(self._spawned_argv))) self._do_magic("resume") else: self._update_status("Spawned `{command}`. Use %resume to let the main thread start executing!".format(command=" ".join(self._spawned_argv))) else: self._clear_status() self._ready.set() def _on_stop(self): def set_return(): raise EOFError() self._stopping.set() if self._cli is not None: try: self._cli.eventloop.call_from_executor(set_return) except Exception: pass def _stop(self): self._unload_script() self._unmonitor_script() def _load_script(self): self._monitor_script() self._seqno += 1 script = self._session.create_script(name="repl%d" % self._seqno, source=self._create_repl_script()) script.set_log_handler(self._log) self._unload_script() self._script = script def on_message(message, data): self._reactor.schedule(lambda: self._process_message(message, data)) script.on('message', on_message) script.load() def _unload_script(self): if self._script is None: return try: self._script.unload() except: pass self._script = None def _monitor_script(self): if self._monitored_file == self._user_script: return self._unmonitor_script() if self._user_script is not None: monitor = frida.FileMonitor(self._user_script) monitor.on('change', self._on_change) monitor.enable() self._script_monitor = monitor self._monitored_file = self._user_script def _unmonitor_script(self): if self._script_monitor is None: return self._script_monitor.disable() self._script_monitor = None def _process_input(self, reactor): if not self._quiet: self._print_startup_message() while self._ready.wait(0.5) != True: if not reactor.is_running(): return have_terminal = sys.stdin.isatty() and sys.stdout.isatty() while True: expression = "" line = "" while len(expression) == 0 or line.endswith("\\"): if not reactor.is_running(): return prompt = "[%s]" % self._prompt_string + "-> " if len(expression) == 0 else "... " pending_eval = self._pending_eval if pending_eval is not None: if len(pending_eval) > 0: expression = pending_eval.pop(0) if not self._quiet: self._print(prompt + expression) else: self._pending_eval = None else: if self._quiet: self._exit_status = 0 if self._errors == 0 else 1 return try: if have_terminal: # We create the prompt manually instead of using get_input, # so we can use the cli in the _on_stop method eventloop = create_eventloop() self._cli = CommandLineInterface( application=create_prompt_application(prompt, history=self._history, completer=self._completer, lexer=JavascriptLexer), eventloop=eventloop, output=create_output()) try: line = None document = self._cli.run() if document: line = document.text finally: eventloop.close() else: line = get_input() except EOFError: if have_terminal: self._print("\nThank you for using Frida!") else: while not self._stopping.wait(1): pass return except KeyboardInterrupt: line = "" continue if len(line.strip()) > 0: if len(expression) > 0: expression += "\n" expression += line.rstrip("\\") if expression.endswith("?"): try: self._print_help(expression) except JavaScriptError as e: error = e.error self._print(Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message']) except frida.InvalidOperationError: return elif expression.startswith("%"): self._do_magic(expression[1:].rstrip()) elif expression in ("exit", "quit", "q"): self._print("Thank you for using Frida!") return elif expression == "help": self._print("Help: #TODO :)") else: if not self._eval_and_print(expression): self._errors += 1 def _eval_and_print(self, expression): success = False try: (t, value) = self._evaluate(expression) if t in ('function', 'undefined', 'null'): output = t elif t == 'binary': output = hexdump(value).rstrip("\n") else: output = json.dumps(value, sort_keys=True, indent=4, separators=(",", ": ")) success = True except JavaScriptError as e: error = e.error output = Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message'] except frida.InvalidOperationError: return success self._print(output) return success def _print_startup_message(self): self._print("""\ ____ / _ | Frida {version} - A world-class dynamic instrumentation framework | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/""".format(version=frida.__version__)) def _print_help(self, expression): # TODO: Figure out docstrings and implement here. This is real jankaty right now. help_text = "" if expression.endswith(".?"): expression = expression[:-2] + "?" obj_to_identify = [x for x in expression.split(' ') if x.endswith("?")][0][:-1] (obj_type, obj_value) = self._evaluate(obj_to_identify) if obj_type == "function": signature = self._evaluate("%s.toString()" % obj_to_identify)[1] clean_signature = signature.split("{")[0][:-1].split('function ')[-1] if "[native code]" in signature: help_text += "Type: Function (native)\n" else: help_text += "Type: Function\n" help_text += "Signature: %s\n" % clean_signature help_text += "Docstring: #TODO :)" elif obj_type == "object": help_text += "Type: Object\n" help_text += "Docstring: #TODO :)" elif obj_type == "boolean": help_text += "Type: Boolean\n" help_text += "Docstring: #TODO :)" elif obj_type == "string": help_text += "Type: Boolean\n" help_text += "Text: %s\n" % self._evaluate("%s.toString()" % obj_to_identify)[1] help_text += "Docstring: #TODO :)" self._print(help_text) # Negative means at least abs(val) - 1 _magic_command_args = { 'resume': 0, 'load': 1, 'reload': 0, 'unload': 0, 'time': -2 # At least 1 arg } def _do_magic(self, statement): tokens = statement.split(" ") command = tokens[0] args = tokens[1:] required_args = self._magic_command_args.get(command) if required_args == None: self._print("Unknown command: {}".format(command)) self._print("Valid commands: {}".format(", ".join(self._magic_command_args.keys()))) return atleast_args = False if required_args < 0: atleast_args = True required_args = abs(required_args) - 1 if (not atleast_args and len(args) != required_args) or \ (atleast_args and len(args) < required_args): self._print("{cmd} command expects {atleast}{n} argument{s}".format( cmd=command, atleast='atleast ' if atleast_args else '', n=required_args, s='' if required_args == 1 else ' ')) return if command == 'resume': self._reactor.schedule(lambda: self._resume()) elif command == 'load': old_user_script = self._user_script self._user_script = os.path.abspath(args[0]) if not self._reload(): self._user_script = old_user_script elif command == 'reload': self._reload() elif command == 'unload': self._user_script = None self._reload() elif command == 'time': self._eval_and_print(''' (function () { var _startTime = Date.now(); var _result = eval({expression}); var _endTime = Date.now(); console.log('Time: ' + (_endTime - _startTime).toLocaleString() + ' ms.'); return _result; })();'''.format(expression=json.dumps(' '.join(args)))) def _reload(self): completed = threading.Event() result = [None] def do_reload(): try: self._load_script() except Exception as e: result[0] = e completed.set() self._reactor.schedule(do_reload) completed.wait() if result[0] is None: return True else: self._print("Failed to load script: {error}".format(error=result[0])) return False def _create_prompt(self): device_type = self._device.type type_name = self._target[0] if type_name == 'pid': if self._target[1] == 0: target = 'SystemSession' else: target = 'PID::%u' % self._target[1] elif type_name == 'file': target = os.path.basename(self._target[1][0]) else: target = self._target[1] if device_type in ('local', 'remote'): prompt_string = "%s::%s" % (device_type.title(), target) else: prompt_string = "%s::%s" % (self._device.name, target) return prompt_string def _evaluate(self, text): result = self._script.exports.evaluate(text) if is_byte_array(result): return ('binary', result) elif isinstance(result, dict): return ('binary', bytes()) elif result[0] == 'error': raise JavaScriptError(result[1]) else: return result def _process_message(self, message, data): message_type = message['type'] if message_type == 'error': text = message.get('stack', message['description']) self._log('error', text) self._errors += 1 else: self._print("message:", message, "data:", data) def _on_change(self, changed_file, other_file, event_type): if event_type == 'changes-done-hint': return self._last_change_id += 1 change_id = self._last_change_id self._reactor.schedule(lambda: self._process_change(change_id), delay=0.05) def _process_change(self, change_id): if change_id != self._last_change_id: return try: self._load_script() except Exception as e: self._print("Failed to load script: {error}".format(error=e)) def _create_repl_script(self): user_script = "" if self._codeshare_script is not None: user_script = self._codeshare_script if self._user_script is not None: with codecs.open(self._user_script, 'rb', 'utf-8') as f: user_script += f.read().rstrip("\r\n") + "\n\n// Frida REPL script:\n" return user_script + """\ rpc.exports.evaluate = function (expression) { try { var result = (1, eval)(expression); if (result instanceof ArrayBuffer) { return result; } else { var type = (result === null) ? 'null' : typeof result; return [type, result]; } } catch (e) { return ['error', { name: e.name, message: e.message, stack: e.stack }]; } }; """ def _load_codeshare_script(self, uri): trust_store = self._get_or_create_truststore() project_url = "https://codeshare.frida.re/api/project/{}/".format(uri) response_json = None try: request = build_opener() request.addheaders = [('User-Agent', 'Frida v{} | {}'.format(frida.__version__, platform.platform()))] response = request.open(project_url) response_content = response.read().decode('utf-8') response_json = json.loads(response_content) except Exception as e: self._print("Got an unhandled exception while trying to retrieve {} - {}".format(uri, e)) return None trusted_signature = trust_store.get(uri, "") fingerprint = hashlib.sha256(response_json['source'].encode('utf-8')).hexdigest() if fingerprint == trusted_signature: return response_json['source'] self._print("""Hello! This is the first time you're running this particular snippet, or the snippet's source code has changed. Project Name: {project_name} Author: {author} Slug: {slug} Fingerprint: {fingerprint} URL: {url} """.format( project_name=response_json['project_name'], author="@" + uri.split('/')[0], slug=uri, fingerprint=fingerprint, url="https://codeshare.frida.re/@{}".format(uri) )) while True: prompt_string = "Are you sure you'd like to trust this project? [y/N] " response = get_input(prompt_string) if response.lower() in ('n', 'no') or response == '': return None if response.lower() in ('y', 'yes'): self._print("Adding fingerprint {} to the trust store! You won't be prompted again unless the code changes.".format(fingerprint)) script = response_json['source'] self._update_truststore({ uri: fingerprint }) return script def _get_or_create_config_dir(self): config_dir = os.path.join(os.path.expanduser('~'), '.frida') if not os.path.exists(config_dir): os.makedirs(config_dir) return config_dir def _update_truststore(self, record): trust_store = self._get_or_create_truststore() trust_store.update(record) config_dir = self._get_or_create_config_dir() codeshare_trust_store = os.path.join(config_dir, "codeshare-truststore.json") with open(codeshare_trust_store, 'w') as f: f.write(json.dumps(trust_store)) def _get_or_create_truststore(self): config_dir = self._get_or_create_config_dir() codeshare_trust_store = os.path.join(config_dir, "codeshare-truststore.json") if os.path.exists(codeshare_trust_store): try: with open(codeshare_trust_store) as f: trust_store = json.load(f) except Exception as e: self._print("Unable to load the codeshare truststore ({}), defaulting to an empty truststore. You will be prompted every time you want to run a script!".format(e)) trust_store = {} else: with open(codeshare_trust_store, 'w') as f: f.write(json.dumps({})) trust_store = {} return trust_store
class TerminalInteractiveShell(InteractiveShell): space_for_menu = Integer( 6, help='Number of line at the bottom of the screen ' 'to reserve for the completion menu').tag(config=True) def _space_for_menu_changed(self, old, new): self._update_layout() pt_cli = None debugger_history = None _pt_app = None simple_prompt = Bool( _use_simple_prompt, help= """Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: IPython own testing machinery, and emacs inferior-shell integration through elpy. This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` environment variable is set, or the current terminal is not a tty. """).tag(config=True) @property def debugger_cls(self): return Pdb if self.simple_prompt else TerminalPdb confirm_exit = Bool( True, help=""" Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a direct exit without any confirmation.""", ).tag(config=True) editing_mode = Unicode( 'emacs', help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", ).tag(config=True) mouse_support = Bool( False, help="Enable mouse support in the prompt").tag(config=True) highlighting_style = Unicode( 'legacy', help= "The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())).tag(config=True) @observe('highlighting_style') @observe('colors') def _highlighting_style_changed(self, change): self.refresh_style() def refresh_style(self): self._style = self._make_style_from_name(self.highlighting_style) highlighting_style_overrides = Dict( help="Override highlighting format for specific tokens").tag( config=True) editor = Unicode( get_default_editor(), help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ).tag(config=True) prompts_class = Type( Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag( config=True) prompts = Instance(Prompts) @default('prompts') def _prompts_default(self): return self.prompts_class(self) @observe('prompts') def _(self, change): self._update_layout() @default('displayhook_class') def _displayhook_class_default(self): return RichPromptDisplayHook term_title = Bool( True, help="Automatically set the terminal title").tag(config=True) # Leaving that for beta/rc tester, shoudl remove for 5.0.0 final. display_completions_in_columns = Bool(None, help="DEPRECATED", allow_none=True).tag(config=True) @observe('display_completions_in_columns') def _display_completions_in_columns_changed(self, new): raise DeprecationWarning( "The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`" "with the following acceptable value: 'column', 'multicolumn','readlinelike'. " ) display_completions = Enum(('column', 'multicolumn', 'readlinelike'), default_value='multicolumn').tag(config=True) highlight_matching_brackets = Bool( True, help="Highlight matching brackets .", ).tag(config=True) @observe('term_title') def init_term_title(self, change=None): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def init_display_formatter(self): super(TerminalInteractiveShell, self).init_display_formatter() # terminal only supports plain text self.display_formatter.active_types = ['text/plain'] def init_prompt_toolkit_cli(self): if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if b.complete_state: cc = b.complete_state.current_completion if cc: b.apply_completion(cc) else: b.cancel_completion() return if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text + '\n') if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _previous_history_or_previous_completion(event): """ Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. If completer is open this still select previous completion. """ event.current_buffer.auto_up() @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _next_history_or_next_completion(event): """ Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. If completer is open this still select next completion. """ event.current_buffer.auto_down() @kbmanager.registry.add_binding(Keys.ControlG, filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions())) def _dismiss_completion(event): b = event.current_buffer if b.complete_state: b.cancel_completion() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): b = event.current_buffer if b.complete_state: b.cancel_completion() else: b.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws)) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) if self.display_completions == 'readlinelike': @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & ~cursor_in_leading_ws)) def _disaply_compl(ev): display_completions_like_readline(ev) if sys.platform == 'win32': from IPython.lib.clipboard import (ClipboardEmpty, win32_clipboard_get, tkinter_clipboard_get) @kbmanager.registry.add_binding(Keys.ControlV, filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode())) def _paste(event): try: text = win32_clipboard_get() except TryNext: try: text = tkinter_clipboard_get() except (TryNext, ClipboardEmpty): return except ClipboardEmpty: return event.current_buffer.insert_text(text.replace('\t', ' ' * 4)) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._pt_app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self._eventloop) def _make_style_from_name(self, name): """ Small wrapper that make an IPython compatible style from a style name We need that to add style for prompt ... etc. """ style_overrides = {} if name == 'legacy': legacy = self.colors.lower() if legacy == 'linux': style_cls = get_style_by_name('monokai') style_overrides = _style_overrides_linux elif legacy == 'lightbg': style_overrides = _style_overrides_light_bg style_cls = get_style_by_name('pastie') elif legacy == 'neutral': # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_cls = get_style_by_name('default') style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', }) elif legacy == 'nocolor': style_cls = _NoStyle style_overrides = {} else: raise ValueError('Got unknown colors: ', legacy) else: style_cls = get_style_by_name(name) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', } style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) return style def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer': IPythonPTLexer(), 'reserve_space_for_menu': self.space_for_menu, 'get_prompt_tokens': self.prompts.in_prompt_tokens, 'get_continuation_tokens': self.prompts.continuation_prompt_tokens, 'multiline': True, 'display_completions_in_columns': (self.display_completions == 'multicolumn'), # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'extra_input_processors': [ ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: self.highlight_matching_brackets)) ], } def _update_layout(self): """ Ask for a re computation of the application layout, if for example , some configuration options have changed. """ if self._pt_app: self._pt_app.layout = create_prompt_layout( **self._layout_options()) def prompt_for_code(self): document = self.pt_cli.run(pre_run=self.pre_prompt, reset_current_buffer=True) return document.text def init_io(self): if sys.platform not in {'win32', 'cli'}: return import win_unicode_console import colorama win_unicode_console.enable() colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 from IPython.utils import io io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True self.debugger_history = InMemoryHistory() def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: self.pt_cli.application.buffer.text = cast_unicode_py2( self.rl_next_input) self.rl_next_input = None def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED): if display_banner is not DISPLAY_BANNER_DEPRECATED: warn( 'interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if (not self.confirm_exit) \ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. if display_banner is not DISPLAY_BANNER_DEPRECATED: warn( 'mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") if hasattr(self, '_eventloop'): self._eventloop.close() _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) def enable_gui(self, gui=None): if gui: self._inputhook = get_inputhook_func(gui) else: self._inputhook = None # Run !system commands directly, not through pipes, so terminal programs # work correctly. system = InteractiveShell.system_raw def auto_rewrite_input(self, cmd): """Overridden from the parent class to use fancy rewriting prompt""" if not self.show_rewritten_input: return tokens = self.prompts.rewrite_prompt_tokens() if self.pt_cli: self.pt_cli.print_tokens(tokens) print(cmd) else: prompt = ''.join(s for t, s in tokens) print(prompt, cmd, sep='') _prompts_before = None def switch_doctest_mode(self, mode): """Switch prompts to classic for %doctest_mode""" if mode: self._prompts_before = self.prompts self.prompts = ClassicPrompts(self) elif self._prompts_before: self.prompts = self._prompts_before self._prompts_before = None self._update_layout()
class Editor(object): """Gui Editor for AIML gen buffer -> layout -> application -> cli """ def __init__(self): """docstring for __init__""" self._key_handlers = KeyHandlers() self._loop = create_eventloop() self._key_binding_mgr = self._key_handlers.get_key_binding_manager() self._key_binding_registry = self._key_handlers.get_key_binding_registry() self._application = None self._cli = None self._editor_title = egc.EDITOR_TITLE self._on_editor_started = None self._on_editor_rendered = None self._on_editor_content_changed = None self._editor_buffer = None self._editor_buffer_name = 'MainBuffer' self._completer_list = ['abc', 'def'] self._left_margin = None self._right_margin = None def setup_buffer(self): """docstring for setup_buffer""" completer = None auto_suggest = None history = None if bool(egc.BUFFER_COMPLETER) is True: completer = WordCompleter(self._completer_list) if bool(egc.BUFFER_AUTO_SUGGEST) is True: auto_suggest = AutoSuggestFromHistory() if bool(egc.BUFFER_HISTORY) is True: history = FileHistory('history.temp') self._editor_buffer = Buffer( completer=completer, auto_suggest=auto_suggest, history=history, tempfile_suffix=bool(egc.BUFFER_TEMP_FILE_SUFFIX), is_multiline=bool(egc.BUFFER_MULTILINE), complete_while_typing=bool(egc.BUFFER_COMPLETE_WHILE_TYPING), enable_history_search=bool(egc.BUFFER_ENABLE_HIST_SEARCH) ) def setup_cli(self): """docstring for run""" self._cli = CommandLineInterface(application=self._application, eventloop=self._loop) self._cli.add_buffer(self._editor_buffer_name, self._editor_buffer) #, focus=True) def setup_layout(self): """docstring for setup_layout""" if bool(egc.TEXT_EDITOR_ENABLE_LEFT_MARGIN) is True: self._left_margin = NumberredMargin(display_tildes=True) if bool(egc.TEXT_EDITOR_ENABLE_RIGHT_MARGIN) is True: self._right_margin = ScrollbarMargin(display_arrows=True) self._buffer_control = BufferControl(buffer_name=self._editor_buffer_name) self._editor_aiml_code_window = Window(content=self._buffer_control, left_margins=[self._left_margin,]) self._vertical_line = FillControl('|', token=Token.Line) self._window_separater = Window(width=D.exact(1), content=self._vertical_line) self._aiml_list = TokenListControl(get_tokens=self.get_aiml_list) self._editor_aiml_list_window = Window(content=self._aiml_list, right_margins=[self._right_margin,]) self._layout = VSplit([ self._editor_aiml_code_window, self._window_separater, self._editor_aiml_list_window, ]) def get_editor_title(self): """docstring for get_editor_title""" return self._editor_title def get_aiml_list(self, cli): """docstring for get_aiml_list""" return [ (Token.ABC, 'abc'), (Token.DEF, 'def') ] def editor_started_callback(self, cli): """docstring for editor_started_callback""" if self._on_editor_started is not None and\ callable(self._on_editor_started): self._on_editor_started(cli) def editor_rendered_callback(self, cli): """docstring for editor_rendered_callback""" if self._on_editor_rendered is not None and\ callable(self._on_editor_rendered): self._on_editor_rendered(cli) def editor_content_changed(self, cli): """docstring for editor_content_changed""" if self._on_editor_content_changed is not None and\ callable(self._on_editor_content_changed): self._on_editor_content_changed(cli) def setup_application(self): """docstring for setup_application""" self._clipboard = InMemoryClipboard() self._application = Application(key_bindings_registry=self._key_binding_registry, layout=self._layout, mouse_support=bool(egc.EDITOR_MOUSE_SUPPORT), use_alternate_screen=True, initial_focussed_buffer=self._editor_buffer_name, clipboard=self._clipboard, get_title=self.get_editor_title, editing_mode=EditingMode.VI if egc.EDITOR_EDITING_MODE == 'VI' else EditingMode.EMACS, on_initialize=self.editor_started_callback, on_render=self.editor_rendered_callback, on_buffer_changed=self.editor_content_changed )
def init_prompt_toolkit_cli(self): if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if b.complete_state: cc = b.complete_state.current_completion if cc: b.apply_completion(cc) else: b.cancel_completion() return if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text + '\n') if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _previous_history_or_previous_completion(event): """ Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. If completer is open this still select previous completion. """ event.current_buffer.auto_up() @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _next_history_or_next_completion(event): """ Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. If completer is open this still select next completion. """ event.current_buffer.auto_down() @kbmanager.registry.add_binding(Keys.ControlG, filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions())) def _dismiss_completion(event): b = event.current_buffer if b.complete_state: b.cancel_completion() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): b = event.current_buffer if b.complete_state: b.cancel_completion() else: b.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws)) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) if self.display_completions == 'readlinelike': @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & ~cursor_in_leading_ws)) def _disaply_compl(ev): display_completions_like_readline(ev) if sys.platform == 'win32': from IPython.lib.clipboard import (ClipboardEmpty, win32_clipboard_get, tkinter_clipboard_get) @kbmanager.registry.add_binding(Keys.ControlV, filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode())) def _paste(event): try: text = win32_clipboard_get() except TryNext: try: text = tkinter_clipboard_get() except (TryNext, ClipboardEmpty): return except ClipboardEmpty: return event.current_buffer.insert_text(text.replace('\t', ' ' * 4)) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._pt_app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self._eventloop)
class TerminalInteractiveShell(InteractiveShell): colors_force = True space_for_menu = Integer(6, config=True, help='Number of line at the bottom of the screen ' 'to reserve for the completion menu') def _space_for_menu_changed(self, old, new): self._update_layout() pt_cli = None autoedit_syntax = CBool(False, config=True, help="auto editing of files with syntax errors.") confirm_exit = CBool(True, config=True, help=""" Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a direct exit without any confirmation.""", ) vi_mode = Bool(False, config=True, help="Use vi style keybindings at the prompt", ) mouse_support = Bool(False, config=True, help="Enable mouse support in the prompt" ) highlighting_style = Unicode('default', config=True, help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles()) ) def _highlighting_style_changed(self, old, new): self._style = self._make_style_from_name(self.highlighting_style) highlighting_style_overrides = Dict(config=True, help="Override highlighting format for specific tokens" ) editor = Unicode(get_default_editor(), config=True, help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ) term_title = Bool(True, config=True, help="Automatically set the terminal title" ) def _term_title_changed(self, name, new_value): self.init_term_title() def init_term_title(self): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def get_prompt_tokens(self, cli): return [ (Token.Prompt, 'In ['), (Token.PromptNum, str(self.execution_count)), (Token.Prompt, ']: '), ] def get_continuation_tokens(self, cli, width): return [ (Token.Prompt, (' ' * (width - 5)) + '...: '), ] def init_prompt_toolkit_cli(self): if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty(): # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode) insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT) # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws )) def _(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) self._app = create_prompt_application( key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options() ) self.pt_cli = CommandLineInterface(self._app, eventloop=create_eventloop(self.inputhook)) def _make_style_from_name(self, name): """ Small wrapper that make an IPython compatible style from a style name We need that to add style for prompt ... etc. """ style_cls = get_style_by_name(name) style_overrides = { Token.Prompt: style_cls.styles.get( Token.Keyword, '#009900'), Token.PromptNum: style_cls.styles.get( Token.Literal.Number, '#00ff00 bold') } if name is 'default': style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) return style def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer':IPythonPTLexer(), 'reserve_space_for_menu':self.space_for_menu, 'get_prompt_tokens':self.get_prompt_tokens, 'get_continuation_tokens':self.get_continuation_tokens, 'multiline':False, } def _update_layout(self): """ Ask for a re computation of the application layout, if for example , some configuration options have changed. """ self._app.layout = create_prompt_layout(**self._layout_options()) def prompt_for_code(self): document = self.pt_cli.run(pre_run=self.pre_prompt) return document.text def init_io(self): if sys.platform not in {'win32', 'cli'}: return import colorama colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 from IPython.utils import io io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input) self.rl_next_input = None def interact(self): while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if (not self.confirm_exit) \ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) if self.autoedit_syntax and self.SyntaxTB.last_syntax_error: self.edit_syntax_error() def mainloop(self): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) def enable_gui(self, gui=None): if gui: self._inputhook = get_inputhook_func(gui) else: self._inputhook = None # Methods to support auto-editing of SyntaxErrors: def edit_syntax_error(self): """The bottom half of the syntax error handler called in the main loop. Loop until syntax error is fixed or user cancels. """ while self.SyntaxTB.last_syntax_error: # copy and clear last_syntax_error err = self.SyntaxTB.clear_err_state() if not self._should_recompile(err): return try: # may set last_syntax_error again if a SyntaxError is raised self.safe_execfile(err.filename, self.user_ns) except: self.showtraceback() else: try: with open(err.filename) as f: # This should be inside a display_trap block and I # think it is. sys.displayhook(f.read()) except: self.showtraceback() def _should_recompile(self, e): """Utility routine for edit_syntax_error""" if e.filename in ('<ipython console>', '<input>', '<string>', '<console>', '<BackgroundJob compilation>', None): return False try: if (self.autoedit_syntax and not self.ask_yes_no( 'Return to editor to correct syntax error? ' '[Y/n] ', 'y')): return False except EOFError: return False def int0(x): try: return int(x) except TypeError: return 0 # always pass integer line and offset values to editor hook try: self.hooks.fix_error_editor(e.filename, int0(e.lineno), int0(e.offset), e.msg) except TryNext: warn('Could not open editor') return False return True # Run !system commands directly, not through pipes, so terminal programs # work correctly. system = InteractiveShell.system_raw
class TerminalInteractiveShell(InteractiveShell): space_for_menu = Integer(6, help='Number of line at the bottom of the screen ' 'to reserve for the completion menu' ).tag(config=True) def _space_for_menu_changed(self, old, new): self._update_layout() pt_cli = None debugger_history = None _pt_app = None simple_prompt = Bool(_use_simple_prompt, help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: IPython own testing machinery, and emacs inferior-shell integration through elpy. This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` environment variable is set, or the current terminal is not a tty. """ ).tag(config=True) @property def debugger_cls(self): return Pdb if self.simple_prompt else TerminalPdb confirm_exit = Bool(True, help=""" Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a direct exit without any confirmation.""", ).tag(config=True) editing_mode = Unicode('emacs', help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", ).tag(config=True) mouse_support = Bool(False, help="Enable mouse support in the prompt" ).tag(config=True) highlighting_style = Union([Unicode('legacy'), Type(klass=Style)], help="""The name or class of a Pygments style to use for syntax highlighting: \n %s""" % ', '.join(get_all_styles()) ).tag(config=True) @observe('highlighting_style') @observe('colors') def _highlighting_style_changed(self, change): self.refresh_style() def refresh_style(self): self._style = self._make_style_from_name_or_cls(self.highlighting_style) highlighting_style_overrides = Dict( help="Override highlighting format for specific tokens" ).tag(config=True) true_color = Bool(False, help=("Use 24bit colors instead of 256 colors in prompt highlighting. " "If your terminal supports true color, the following command " "should print 'TRUECOLOR' in orange: " "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"") ).tag(config=True) editor = Unicode(get_default_editor(), help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ).tag(config=True) prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True) prompts = Instance(Prompts) @default('prompts') def _prompts_default(self): return self.prompts_class(self) @observe('prompts') def _(self, change): self._update_layout() @default('displayhook_class') def _displayhook_class_default(self): return RichPromptDisplayHook term_title = Bool(True, help="Automatically set the terminal title" ).tag(config=True) display_completions = Enum(('column', 'multicolumn','readlinelike'), help= ( "Options for displaying tab completions, 'column', 'multicolumn', and " "'readlinelike'. These options are for `prompt_toolkit`, see " "`prompt_toolkit` documentation for more information." ), default_value='multicolumn').tag(config=True) highlight_matching_brackets = Bool(True, help="Highlight matching brackets .", ).tag(config=True) @observe('term_title') def init_term_title(self, change=None): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def init_display_formatter(self): super(TerminalInteractiveShell, self).init_display_formatter() # terminal only supports plain text self.display_formatter.active_types = ['text/plain'] def init_prompt_toolkit_cli(self): if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return # Set up keyboard shortcuts kbmanager = KeyBindingManager.for_prompt() register_ipython_shortcuts(kbmanager.registry, self) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) last_cell = cell self._style = self._make_style_from_name_or_cls(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._pt_app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(shell=self), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options() ) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface( self._pt_app, eventloop=self._eventloop, output=create_output(true_color=self.true_color)) def _make_style_from_name_or_cls(self, name_or_cls): """ Small wrapper that make an IPython compatible style from a style name We need that to add style for prompt ... etc. """ style_overrides = {} if name_or_cls == 'legacy': legacy = self.colors.lower() if legacy == 'linux': style_cls = get_style_by_name('monokai') style_overrides = _style_overrides_linux elif legacy == 'lightbg': style_overrides = _style_overrides_light_bg style_cls = get_style_by_name('pastie') elif legacy == 'neutral': # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_cls = get_style_by_name('default') style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', }) elif legacy =='nocolor': style_cls=_NoStyle style_overrides = {} else : raise ValueError('Got unknown colors: ', legacy) else : if isinstance(name_or_cls, string_types): style_cls = get_style_by_name(name_or_cls) else: style_cls = name_or_cls style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', } style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) return style def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer':IPythonPTLexer(), 'reserve_space_for_menu':self.space_for_menu, 'get_prompt_tokens':self.prompts.in_prompt_tokens, 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, 'multiline':True, 'display_completions_in_columns': (self.display_completions == 'multicolumn'), # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'extra_input_processors': [ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: self.highlight_matching_brackets))], } def _update_layout(self): """ Ask for a re computation of the application layout, if for example , some configuration options have changed. """ if self._pt_app: self._pt_app.layout = create_prompt_layout(**self._layout_options()) def prompt_for_code(self): document = self.pt_cli.run( pre_run=self.pre_prompt, reset_current_buffer=True) return document.text def enable_win_unicode_console(self): if sys.version_info >= (3, 6): # Since PEP 528, Python uses the unicode APIs for the Windows # console by default, so WUC shouldn't be needed. return import win_unicode_console if PY3: win_unicode_console.enable() else: # https://github.com/ipython/ipython/issues/9768 from win_unicode_console.streams import (TextStreamWrapper, stdout_text_transcoded, stderr_text_transcoded) class LenientStrStreamWrapper(TextStreamWrapper): def write(self, s): if isinstance(s, bytes): s = s.decode(self.encoding, 'replace') self.base.write(s) stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded) stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded) win_unicode_console.enable(stdout=stdout_text_str, stderr=stderr_text_str) def init_io(self): if sys.platform not in {'win32', 'cli'}: return self.enable_win_unicode_console() import colorama colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 # io.std* are deprecated, but don't show our own deprecation warnings # during initialization of the deprecated API. with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True self.debugger_history = InMemoryHistory() def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input) self.rl_next_input = None def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED): if display_banner is not DISPLAY_BANNER_DEPRECATED: warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) self.keep_running = True while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if (not self.confirm_exit) \ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. if display_banner is not DISPLAY_BANNER_DEPRECATED: warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2) while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) def enable_gui(self, gui=None): if gui: self._inputhook = get_inputhook_func(gui) else: self._inputhook = None # Run !system commands directly, not through pipes, so terminal programs # work correctly. system = InteractiveShell.system_raw def auto_rewrite_input(self, cmd): """Overridden from the parent class to use fancy rewriting prompt""" if not self.show_rewritten_input: return tokens = self.prompts.rewrite_prompt_tokens() if self.pt_cli: self.pt_cli.print_tokens(tokens) print(cmd) else: prompt = ''.join(s for t, s in tokens) print(prompt, cmd, sep='') _prompts_before = None def switch_doctest_mode(self, mode): """Switch prompts to classic for %doctest_mode""" if mode: self._prompts_before = self.prompts self.prompts = ClassicPrompts(self) elif self._prompts_before: self.prompts = self._prompts_before self._prompts_before = None self._update_layout()
class ZMQTerminalInteractiveShell(SingletonConfigurable): readline_use = False pt_cli = None _executing = False _execution_state = Unicode('') _pending_clearoutput = False _eventloop = None editing_mode = Unicode('emacs', config=True, help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", ) highlighting_style = Unicode('', config=True, help="The name of a Pygments style to use for syntax highlighting" ) highlighting_style_overrides = Dict(config=True, help="Override highlighting format for specific tokens" ) history_load_length = Integer(1000, config=True, help="How many history items to load into memory" ) banner = Unicode('Jupyter console {version}\n\n{kernel_banner}', config=True, help=("Text to display before the first prompt. Will be formatted with " "variables {version} and {kernel_banner}.") ) kernel_timeout = Float(60, config=True, help="""Timeout for giving up on a kernel (in seconds). On first connect and restart, the console tests whether the kernel is running and responsive by sending kernel_info_requests. This sets the timeout in seconds for how long the kernel can take before being presumed dead. """ ) image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'), 'PIL', config=True, allow_none=True, help= """ Handler for image type output. This is useful, for example, when connecting to the kernel in which pylab inline backend is activated. There are four handlers defined. 'PIL': Use Python Imaging Library to popup image; 'stream': Use an external program to show the image. Image will be fed into the STDIN of the program. You will need to configure `stream_image_handler`; 'tempfile': Use an external program to show the image. Image will be saved in a temporally file and the program is called with the temporally file. You will need to configure `tempfile_image_handler`; 'callable': You can set any Python callable which is called with the image data. You will need to configure `callable_image_handler`. """ ) stream_image_handler = List(config=True, help= """ Command to invoke an image viewer program when you are using 'stream' image handler. This option is a list of string where the first element is the command itself and reminders are the options for the command. Raw image data is given as STDIN to the program. """ ) tempfile_image_handler = List(config=True, help= """ Command to invoke an image viewer program when you are using 'tempfile' image handler. This option is a list of string where the first element is the command itself and reminders are the options for the command. You can use {file} and {format} in the string to represent the location of the generated image file and image format. """ ) callable_image_handler = Any(config=True, help= """ Callable object called via 'callable' image handler with one argument, `data`, which is `msg["content"]["data"]` where `msg` is the message from iopub channel. For exmaple, you can find base64 encoded PNG data as `data['image/png']`. If your function can't handle the data supplied, it should return `False` to indicate this. """ ) mime_preference = List( default_value=['image/png', 'image/jpeg', 'image/svg+xml'], config=True, help= """ Preferred object representation MIME type in order. First matched MIME type will be used. """ ) use_kernel_is_complete = Bool(True, config=True, help="""Whether to use the kernel's is_complete message handling. If False, then the frontend will use its own is_complete handler. """ ) kernel_is_complete_timeout = Float(1, config=True, help="""Timeout (in seconds) for giving up on a kernel's is_complete response. If the kernel does not respond at any point within this time, the kernel will no longer be asked if code is complete, and the console will default to the built-in is_complete test. """ ) manager = Instance('jupyter_client.KernelManager', allow_none=True) client = Instance('jupyter_client.KernelClient', allow_none=True) def _client_changed(self, name, old, new): self.session_id = new.session.session session_id = Unicode() def _banner1_default(self): return "Jupyter Console {version}\n".format(version=__version__) def __init__(self, **kwargs): # This is where traits with a config_key argument are updated # from the values on config. super(ZMQTerminalInteractiveShell, self).__init__(**kwargs) self.configurables = [self] self.init_history() self.init_completer() self.init_io() self.init_kernel_info() self.init_prompt_toolkit_cli() self.keep_running = True self.execution_count = 1 def init_completer(self): """Initialize the completion machinery. This creates completion machinery that can be used by client code, either interactively in-process (typically triggered by the readline library), programmatically (such as in test suites) or out-of-process (typically over the network by remote frontends). """ self.Completer = ZMQCompleter(self, self.client, config=self.config) def init_history(self): """Sets up the command history. """ self.history_manager = ZMQHistoryManager(client=self.client) self.configurables.append(self.history_manager) def get_prompt_tokens(self, cli): return [ (Token.Prompt, 'In ['), (Token.PromptNum, str(self.execution_count)), (Token.Prompt, ']: '), ] def get_continuation_tokens(self, cli, width): return [ (Token.Prompt, (' ' * (width - 2)) + ': '), ] def get_out_prompt_tokens(self): return [ (Token.OutPrompt, 'Out['), (Token.OutPromptNum, str(self.execution_count)), (Token.OutPrompt, ']: ') ] def print_out_prompt(self): self.pt_cli.print_tokens(self.get_out_prompt_tokens()) kernel_info = {} def init_kernel_info(self): """Wait for a kernel to be ready, and store kernel info""" timeout = self.kernel_timeout tic = time.time() self.client.hb_channel.unpause() msg_id = self.client.kernel_info() while True: try: reply = self.client.get_shell_msg(timeout=1) except Empty: if (time.time() - tic) > timeout: raise RuntimeError("Kernel didn't respond to kernel_info_request") else: if reply['parent_header'].get('msg_id') == msg_id: self.kernel_info = reply['content'] return def show_banner(self): print(self.banner.format(version=__version__, kernel_banner=self.kernel_info.get('banner', ''))) def init_prompt_toolkit_cli(self): if 'JUPYTER_CONSOLE_TEST' in os.environ: # Simple restricted interface for tests so we can find prompts with # pexpect. Multi-line input not supported. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt self.print_out_prompt = \ lambda: print('Out[%d]: ' % self.execution_count, end='') return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return more, indent = self.check_complete(d.text) if (not more) and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + indent) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#ff2200', Token.OutPromptNum: '#ff0000 bold', } if self.highlighting_style: style_cls = get_style_by_name(self.highlighting_style) else: style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) editing_mode = getattr(EditingMode, self.editing_mode.upper()) langinfo = self.kernel_info.get('language_info', {}) lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text')) app = create_prompt_application(multiline=True, editing_mode=editing_mode, lexer=PygmentsLexer(get_pygments_lexer(lexer)), get_prompt_tokens=self.get_prompt_tokens, get_continuation_tokens=self.get_continuation_tokens, key_bindings_registry=kbmanager.registry, history=history, completer=JupyterPTCompleter(self.Completer), enable_history_search=True, style=style, ) self._eventloop = create_eventloop() self.pt_cli = CommandLineInterface(app, eventloop=self._eventloop) def prompt_for_code(self): document = self.pt_cli.run(pre_run=self.pre_prompt, reset_current_buffer=True) return document.text def init_io(self): if sys.platform not in {'win32', 'cli'}: return import colorama colorama.init() def check_complete(self, code): if self.use_kernel_is_complete: msg_id = self.client.is_complete(code) try: return self.handle_is_complete_reply(msg_id, timeout=self.kernel_is_complete_timeout) except SyntaxError: return False, "" else: more = (code.splitlines()[-1] != "") return more, "" def ask_exit(self): self.keep_running = False # This is set from payloads in handle_execute_reply next_input = None def pre_prompt(self): if self.next_input: b = self.pt_cli.application.buffer b.text = cast_unicode_py2(self.next_input) self.next_input = None # Move the cursor to the end b.cursor_position += b.document.get_end_of_document_position() def interact(self, display_banner=None): while self.keep_running: print('\n', end='') try: code = self.prompt_for_code() except EOFError: if ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) def mainloop(self): self.keepkernel = False # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") self._eventloop.close() if self.keepkernel and not self.own_kernel: print('keeping kernel alive') elif self.keepkernel and self.own_kernel : print("owning kernel, cannot keep it alive") self.client.shutdown() else : print("Shutting down kernel") self.client.shutdown() def run_cell(self, cell, store_history=True): """Run a complete IPython cell. Parameters ---------- cell : str The code (including IPython code such as %magic functions) to run. store_history : bool If True, the raw and translated cell will be stored in IPython's history. For user code calling back into IPython's machinery, this should be set to False. """ if (not cell) or cell.isspace(): # pressing enter flushes any pending display self.handle_iopub() return # flush stale replies, which could have been ignored, due to missed heartbeats while self.client.shell_channel.msg_ready(): self.client.shell_channel.get_msg() # execute takes 'hidden', which is the inverse of store_hist msg_id = self.client.execute(cell, not store_history) # first thing is wait for any side effects (output, stdin, etc.) self._executing = True self._execution_state = "busy" while self._execution_state != 'idle' and self.client.is_alive(): try: self.handle_input_request(msg_id, timeout=0.05) except Empty: # display intermediate print statements, etc. self.handle_iopub(msg_id) except ZMQError as e: # Carry on if polling was interrupted by a signal if e.errno != errno.EINTR: raise # after all of that is done, wait for the execute reply while self.client.is_alive(): try: self.handle_execute_reply(msg_id, timeout=0.05) except Empty: pass else: break self._executing = False #----------------- # message handlers #----------------- def handle_execute_reply(self, msg_id, timeout=None): msg = self.client.shell_channel.get_msg(block=False, timeout=timeout) if msg["parent_header"].get("msg_id", None) == msg_id: self.handle_iopub(msg_id) content = msg["content"] status = content['status'] if status == 'aborted': self.write('Aborted\n') return elif status == 'ok': # handle payloads for item in content.get("payload", []): source = item['source'] if source == 'page': page.page(item['data']['text/plain']) elif source == 'set_next_input': self.next_input = item['text'] elif source == 'ask_exit': self.keepkernel = item.get('keepkernel', False) self.ask_exit() elif status == 'error': pass self.execution_count = int(content["execution_count"] + 1) def handle_is_complete_reply(self, msg_id, timeout=None): """ Wait for a repsonse from the kernel, and return two values: more? - (boolean) should the frontend ask for more input indent - an indent string to prefix the input Overloaded methods may want to examine the comeplete source. Its is in the self._source_lines_buffered list. """ ## Get the is_complete response: msg = None try: msg = self.client.shell_channel.get_msg(block=True, timeout=timeout) except Empty: warn('The kernel did not respond to an is_complete_request. ' 'Setting `use_kernel_is_complete` to False.') self.use_kernel_is_complete = False return False, "" ## Handle response: if msg["parent_header"].get("msg_id", None) != msg_id: warn('The kernel did not respond properly to an is_complete_request: %s.' % str(msg)) return False, "" else: status = msg["content"].get("status", None) indent = msg["content"].get("indent", "") ## Return more? and indent string if status == "complete": return False, indent elif status == "incomplete": return True, indent elif status == "invalid": raise SyntaxError() elif status == "unknown": return False, indent else: warn('The kernel sent an invalid is_complete_reply status: "%s".' % status) return False, indent include_other_output = Bool(False, config=True, help="""Whether to include output from clients other than this one sharing the same kernel. Outputs are not displayed until enter is pressed. """ ) other_output_prefix = Unicode("[remote] ", config=True, help="""Prefix to add to outputs coming from clients other than this one. Only relevant if include_other_output is True. """ ) def from_here(self, msg): """Return whether a message is from this session""" return msg['parent_header'].get("session", self.session_id) == self.session_id def include_output(self, msg): """Return whether we should include a given output message""" from_here = self.from_here(msg) if msg['msg_type'] == 'execute_input': # only echo inputs not from here return self.include_other_output and not from_here if self.include_other_output: return True else: return from_here def handle_iopub(self, msg_id=''): """Process messages on the IOPub channel This method consumes and processes messages on the IOPub channel, such as stdout, stderr, execute_result and status. It only displays output that is caused by this session. """ while self.client.iopub_channel.msg_ready(): sub_msg = self.client.iopub_channel.get_msg() msg_type = sub_msg['header']['msg_type'] parent = sub_msg["parent_header"] if self.include_output(sub_msg): if msg_type == 'status': self._execution_state = sub_msg["content"]["execution_state"] elif msg_type == 'stream': if sub_msg["content"]["name"] == "stdout": if self._pending_clearoutput: print("\r", end="") self._pending_clearoutput = False print(sub_msg["content"]["text"], end="") sys.stdout.flush() elif sub_msg["content"]["name"] == "stderr": if self._pending_clearoutput: print("\r", file=sys.stderr, end="") self._pending_clearoutput = False print(sub_msg["content"]["text"], file=sys.stderr, end="") sys.stderr.flush() elif msg_type == 'execute_result': if self._pending_clearoutput: print("\r", end="") self._pending_clearoutput = False self.execution_count = int(sub_msg["content"]["execution_count"]) if not self.from_here(sub_msg): sys.stdout.write(self.other_output_prefix) format_dict = sub_msg["content"]["data"] self.handle_rich_data(format_dict) if 'text/plain' not in format_dict: continue self.print_out_prompt() text_repr = format_dict['text/plain'] if '\n' in text_repr: # For multi-line results, start a new line after prompt print() print(text_repr) elif msg_type == 'display_data': data = sub_msg["content"]["data"] handled = self.handle_rich_data(data) if not handled: if not self.from_here(sub_msg): sys.stdout.write(self.other_output_prefix) # if it was an image, we handled it by now if 'text/plain' in data: print(data['text/plain']) elif msg_type == 'execute_input': content = sub_msg['content'] if not self.from_here(sub_msg): sys.stdout.write(self.other_output_prefix) sys.stdout.write('In [{}]: '.format(content['execution_count'])) sys.stdout.write(content['code']) elif msg_type == 'clear_output': if sub_msg["content"]["wait"]: self._pending_clearoutput = True else: print("\r", end="") elif msg_type == 'error': for frame in sub_msg["content"]["traceback"]: print(frame, file=sys.stderr) _imagemime = { 'image/png': 'png', 'image/jpeg': 'jpeg', 'image/svg+xml': 'svg', } def handle_rich_data(self, data): for mime in self.mime_preference: if mime in data and mime in self._imagemime: if self.handle_image(data, mime): return True return False def handle_image(self, data, mime): handler = getattr( self, 'handle_image_{0}'.format(self.image_handler), None) if handler: return handler(data, mime) def handle_image_PIL(self, data, mime): if mime not in ('image/png', 'image/jpeg'): return False try: from PIL import Image, ImageShow except ImportError: return False raw = base64.decodestring(data[mime].encode('ascii')) img = Image.open(BytesIO(raw)) return ImageShow.show(img) def handle_image_stream(self, data, mime): raw = base64.decodestring(data[mime].encode('ascii')) imageformat = self._imagemime[mime] fmt = dict(format=imageformat) args = [s.format(**fmt) for s in self.stream_image_handler] with open(os.devnull, 'w') as devnull: proc = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=devnull, stderr=devnull) proc.communicate(raw) return (proc.returncode == 0) def handle_image_tempfile(self, data, mime): raw = base64.decodestring(data[mime].encode('ascii')) imageformat = self._imagemime[mime] filename = 'tmp.{0}'.format(imageformat) with NamedFileInTemporaryDirectory(filename) as f, \ open(os.devnull, 'w') as devnull: f.write(raw) f.flush() fmt = dict(file=f.name, format=imageformat) args = [s.format(**fmt) for s in self.tempfile_image_handler] rc = subprocess.call(args, stdout=devnull, stderr=devnull) return (rc == 0) def handle_image_callable(self, data, mime): res = self.callable_image_handler(data) if res is not False: # If handler func returns e.g. None, assume it has handled the data. res = True return res def handle_input_request(self, msg_id, timeout=0.1): """ Method to capture raw_input """ req = self.client.stdin_channel.get_msg(timeout=timeout) # in case any iopub came while we were waiting: self.handle_iopub(msg_id) if msg_id == req["parent_header"].get("msg_id"): # wrap SIGINT handler real_handler = signal.getsignal(signal.SIGINT) def double_int(sig,frame): # call real handler (forwards sigint to kernel), # then raise local interrupt, stopping local raw_input real_handler(sig,frame) raise KeyboardInterrupt signal.signal(signal.SIGINT, double_int) content = req['content'] read = getpass if content.get('password', False) else input try: raw_data = read(content["prompt"]) except EOFError: # turn EOFError into EOF character raw_data = '\x04' except KeyboardInterrupt: sys.stdout.write('\n') return finally: # restore SIGINT handler signal.signal(signal.SIGINT, real_handler) # only send stdin reply if there *was not* another request # or execution finished while we were reading. if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()): self.client.input(raw_data)
class TerminalInteractiveShell(InteractiveShell): colors_force = True space_for_menu = Integer( 6, help='Number of line at the bottom of the screen ' 'to reserve for the completion menu').tag(config=True) def _space_for_menu_changed(self, old, new): self._update_layout() pt_cli = None debugger_history = None simple_prompt = Bool( _use_simple_prompt, help= """Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: IPython own testing machinery, and emacs inferior-shell integration through elpy. This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` environment variable is set, or the current terminal is not a tty. """).tag(config=True) @property def debugger_cls(self): return Pdb if self.simple_prompt else TerminalPdb autoedit_syntax = Bool( False, help="auto editing of files with syntax errors.", ).tag(config=True) confirm_exit = Bool( True, help=""" Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a direct exit without any confirmation.""", ).tag(config=True) editing_mode = Unicode( 'emacs', help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", ).tag(config=True) mouse_support = Bool( False, help="Enable mouse support in the prompt").tag(config=True) highlighting_style = Unicode( 'default', help= "The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())).tag(config=True) @observe('highlighting_style') def _highlighting_style_changed(self, change): self._style = self._make_style_from_name(self.highlighting_style) highlighting_style_overrides = Dict( help="Override highlighting format for specific tokens").tag( config=True) editor = Unicode( get_default_editor(), help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ).tag(config=True) prompts_class = Type( Prompts, config=True, help='Class used to generate Prompt token for prompt_toolkit') prompts = Instance(Prompts) def _prompts_default(self): return self.prompts_class(self) @observe('prompts') def _(self, change): self._update_layout() def _displayhook_class_default(self): return RichPromptDisplayHook term_title = Bool( True, help="Automatically set the terminal title").tag(config=True) display_completions_in_columns = Bool( False, help="Display a multi column completion menu.", ).tag(config=True) highlight_matching_brackets = Bool( True, help="Highlight matching brackets .", ).tag(config=True) @observe('term_title') def init_term_title(self, change=None): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def init_prompt_toolkit_cli(self): self._app = None if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): event.current_buffer.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws)) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop) def _make_style_from_name(self, name): """ Small wrapper that make an IPython compatible style from a style name We need that to add style for prompt ... etc. """ style_cls = get_style_by_name(name) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', } if name == 'default': style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) return style def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer': IPythonPTLexer(), 'reserve_space_for_menu': self.space_for_menu, 'get_prompt_tokens': self.prompts.in_prompt_tokens, 'get_continuation_tokens': self.prompts.continuation_prompt_tokens, 'multiline': True, 'display_completions_in_columns': self.display_completions_in_columns, # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'extra_input_processors': [ ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: self.highlight_matching_brackets)) ], } def _update_layout(self): """ Ask for a re computation of the application layout, if for example , some configuration options have changed. """ if getattr(self, '._app', None): self._app.layout = create_prompt_layout(**self._layout_options()) def prompt_for_code(self): document = self.pt_cli.run(pre_run=self.pre_prompt, reset_current_buffer=True) return document.text def init_io(self): if sys.platform not in {'win32', 'cli'}: return import colorama colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 from IPython.utils import io io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True self.debugger_history = InMemoryHistory() def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: self.pt_cli.application.buffer.text = cast_unicode_py2( self.rl_next_input) self.rl_next_input = None def interact(self): while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if (not self.confirm_exit) \ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) if self.autoedit_syntax and self.SyntaxTB.last_syntax_error: self.edit_syntax_error() def mainloop(self): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") if hasattr(self, '_eventloop'): self._eventloop.close() _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) def enable_gui(self, gui=None): if gui: self._inputhook = get_inputhook_func(gui) else: self._inputhook = None # Methods to support auto-editing of SyntaxErrors: def edit_syntax_error(self): """The bottom half of the syntax error handler called in the main loop. Loop until syntax error is fixed or user cancels. """ while self.SyntaxTB.last_syntax_error: # copy and clear last_syntax_error err = self.SyntaxTB.clear_err_state() if not self._should_recompile(err): return try: # may set last_syntax_error again if a SyntaxError is raised self.safe_execfile(err.filename, self.user_ns) except: self.showtraceback() else: try: with open(err.filename) as f: # This should be inside a display_trap block and I # think it is. sys.displayhook(f.read()) except: self.showtraceback() def _should_recompile(self, e): """Utility routine for edit_syntax_error""" if e.filename in ('<ipython console>', '<input>', '<string>', '<console>', '<BackgroundJob compilation>', None): return False try: if (self.autoedit_syntax and not self.ask_yes_no( 'Return to editor to correct syntax error? ' '[Y/n] ', 'y')): return False except EOFError: return False def int0(x): try: return int(x) except TypeError: return 0 # always pass integer line and offset values to editor hook try: self.hooks.fix_error_editor(e.filename, int0(e.lineno), int0(e.offset), e.msg) except TryNext: warn('Could not open editor') return False return True # Run !system commands directly, not through pipes, so terminal programs # work correctly. system = InteractiveShell.system_raw def auto_rewrite_input(self, cmd): """Overridden from the parent class to use fancy rewriting prompt""" if not self.show_rewritten_input: return tokens = self.prompts.rewrite_prompt_tokens() if self.pt_cli: self.pt_cli.print_tokens(tokens) print(cmd) else: prompt = ''.join(s for t, s in tokens) print(prompt, cmd, sep='') _prompts_before = None def switch_doctest_mode(self, mode): """Switch prompts to classic for %doctest_mode""" if mode: self._prompts_before = self.prompts self.prompts = ClassicPrompts(self) elif self._prompts_before: self.prompts = self._prompts_before self._prompts_before = None
class TerminalInteractiveShell(InteractiveShell): colors_force = True space_for_menu = Integer(6, help='Number of line at the bottom of the screen ' 'to reserve for the completion menu' ).tag(config=True) def _space_for_menu_changed(self, old, new): self._update_layout() pt_cli = None debugger_history = None simple_prompt = Bool(_use_simple_prompt, help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors. Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are: IPython own testing machinery, and emacs inferior-shell integration through elpy. This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` environment variable is set, or the current terminal is not a tty. """ ).tag(config=True) @property def debugger_cls(self): return Pdb if self.simple_prompt else TerminalPdb autoedit_syntax = Bool(False, help="auto editing of files with syntax errors.", ).tag(config=True) confirm_exit = Bool(True, help=""" Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a direct exit without any confirmation.""", ).tag(config=True) editing_mode = Unicode('emacs', help="Shortcut style to use at the prompt. 'vi' or 'emacs'.", ).tag(config=True) mouse_support = Bool(False, help="Enable mouse support in the prompt" ).tag(config=True) highlighting_style = Unicode('default', help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles()) ).tag(config=True) @observe('highlighting_style') def _highlighting_style_changed(self, change): self._style = self._make_style_from_name(self.highlighting_style) highlighting_style_overrides = Dict( help="Override highlighting format for specific tokens" ).tag(config=True) editor = Unicode(get_default_editor(), help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ).tag(config=True) prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True) prompts = Instance(Prompts) @default('prompts') def _prompts_default(self): return self.prompts_class(self) @observe('prompts') def _(self, change): self._update_layout() @default('displayhook_class') def _displayhook_class_default(self): return RichPromptDisplayHook term_title = Bool(True, help="Automatically set the terminal title" ).tag(config=True) display_completions_in_columns = Bool(False, help="Display a multi column completion menu.", ).tag(config=True) highlight_matching_brackets = Bool(True, help="Highlight matching brackets .", ).tag(config=True) @observe('term_title') def init_term_title(self, change=None): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def init_prompt_toolkit_cli(self): self._app = None if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if b.complete_state: cc = b.complete_state.current_completion if cc: b.apply_completion(cc) else: b.cancel_completion() return if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _previous_history_or_previous_completion(event): """ Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. If completer is open this still select previous completion. """ event.current_buffer.auto_up() @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _next_history_or_next_completion(event): """ Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. If completer is open this still select next completion. """ event.current_buffer.auto_down() @kbmanager.registry.add_binding(Keys.ControlG, filter=( HasFocus(DEFAULT_BUFFER) & HasCompletions() )) def _dismiss_completion(event): b = event.current_buffer if b.complete_state: b.cancel_completion() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): b = event.current_buffer if b.complete_state: b.cancel_completion() else: b.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws )) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options() ) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop) def _make_style_from_name(self, name): """ Small wrapper that make an IPython compatible style from a style name We need that to add style for prompt ... etc. """ style_cls = get_style_by_name(name) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#990000', Token.OutPromptNum: '#ff0000 bold', } if name == 'default': style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) return style def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer':IPythonPTLexer(), 'reserve_space_for_menu':self.space_for_menu, 'get_prompt_tokens':self.prompts.in_prompt_tokens, 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, 'multiline':True, 'display_completions_in_columns': self.display_completions_in_columns, # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'extra_input_processors': [ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: self.highlight_matching_brackets))], } def _update_layout(self): """ Ask for a re computation of the application layout, if for example , some configuration options have changed. """ if getattr(self, '._app', None): self._app.layout = create_prompt_layout(**self._layout_options()) def prompt_for_code(self): document = self.pt_cli.run( pre_run=self.pre_prompt, reset_current_buffer=True) return document.text def init_io(self): if sys.platform not in {'win32', 'cli'}: return import colorama colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 from IPython.utils import io io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True self.debugger_history = InMemoryHistory() def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input) self.rl_next_input = None def interact(self): while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if (not self.confirm_exit) \ or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) if self.autoedit_syntax and self.SyntaxTB.last_syntax_error: self.edit_syntax_error() def mainloop(self): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") if hasattr(self, '_eventloop'): self._eventloop.close() _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) def enable_gui(self, gui=None): if gui: self._inputhook = get_inputhook_func(gui) else: self._inputhook = None # Methods to support auto-editing of SyntaxErrors: def edit_syntax_error(self): """The bottom half of the syntax error handler called in the main loop. Loop until syntax error is fixed or user cancels. """ while self.SyntaxTB.last_syntax_error: # copy and clear last_syntax_error err = self.SyntaxTB.clear_err_state() if not self._should_recompile(err): return try: # may set last_syntax_error again if a SyntaxError is raised self.safe_execfile(err.filename, self.user_ns) except: self.showtraceback() else: try: with open(err.filename) as f: # This should be inside a display_trap block and I # think it is. sys.displayhook(f.read()) except: self.showtraceback() def _should_recompile(self, e): """Utility routine for edit_syntax_error""" if e.filename in ('<ipython console>', '<input>', '<string>', '<console>', '<BackgroundJob compilation>', None): return False try: if (self.autoedit_syntax and not self.ask_yes_no( 'Return to editor to correct syntax error? ' '[Y/n] ', 'y')): return False except EOFError: return False def int0(x): try: return int(x) except TypeError: return 0 # always pass integer line and offset values to editor hook try: self.hooks.fix_error_editor(e.filename, int0(e.lineno), int0(e.offset), e.msg) except TryNext: warn('Could not open editor') return False return True # Run !system commands directly, not through pipes, so terminal programs # work correctly. system = InteractiveShell.system_raw def auto_rewrite_input(self, cmd): """Overridden from the parent class to use fancy rewriting prompt""" if not self.show_rewritten_input: return tokens = self.prompts.rewrite_prompt_tokens() if self.pt_cli: self.pt_cli.print_tokens(tokens) print(cmd) else: prompt = ''.join(s for t, s in tokens) print(prompt, cmd, sep='') _prompts_before = None def switch_doctest_mode(self, mode): """Switch prompts to classic for %doctest_mode""" if mode: self._prompts_before = self.prompts self.prompts = ClassicPrompts(self) elif self._prompts_before: self.prompts = self._prompts_before self._prompts_before = None
def init_prompt_toolkit_cli(self): if 'JUPYTER_CONSOLE_TEST' in os.environ: # Simple restricted interface for tests so we can find prompts with # pexpect. Multi-line input not supported. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt self.print_out_prompt = \ lambda: print('Out[%d]: ' % self.execution_count, end='') return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return more, indent = self.check_complete(d.text) if (not more) and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + indent) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#ff2200', Token.OutPromptNum: '#ff0000 bold', } if self.highlighting_style: style_cls = get_style_by_name(self.highlighting_style) else: style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) editing_mode = getattr(EditingMode, self.editing_mode.upper()) langinfo = self.kernel_info.get('language_info', {}) lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text')) app = create_prompt_application(multiline=True, editing_mode=editing_mode, lexer=PygmentsLexer(get_pygments_lexer(lexer)), get_prompt_tokens=self.get_prompt_tokens, get_continuation_tokens=self.get_continuation_tokens, key_bindings_registry=kbmanager.registry, history=history, completer=JupyterPTCompleter(self.Completer), enable_history_search=True, style=style, ) self._eventloop = create_eventloop() self.pt_cli = CommandLineInterface(app, eventloop=self._eventloop)
class TerminalPdb(Pdb): def __init__(self, *args, **kwargs): Pdb.__init__(self, *args, **kwargs) self._ptcomp = None self.pt_init() def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) if self._ptcomp is None: compl = IPCompleter(shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout) kbmanager = KeyBindingManager.for_prompt() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend )(suspend_to_bg) self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings_registry=kbmanager.registry, history=self.shell.debugger_history, completer= self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens ) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop) def cmdloop(self, intro=None): """Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. override the same methods from cmd.Cmd to provide prompt toolkit replacement. """ if not self.use_rawinput: raise ValueError('Sorry ipdb does not support use_rawinput=False') self.preloop() try: if intro is not None: self.intro = intro if self.intro: self.stdout.write(str(self.intro)+"\n") stop = None while not stop: if self.cmdqueue: line = self.cmdqueue.pop(0) else: self._ptcomp.ipy_completer.namespace = self.curframe_locals self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals try: line = self.pt_cli.run(reset_current_buffer=True).text except EOFError: line = 'EOF' line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) self.postloop() except Exception: raise
def create_cli(self, connection, output, input=None): """ Create `CommandLineInterface` instance for this connection. """ def get_title(): return self.get_title(cli) def on_focus_changed(): """ When the focus changes to a read/write buffer, make sure to go to insert mode. This happens when the ViState was set to NAVIGATION in the copy buffer. """ vi_state = self.key_bindings_manager.pt_key_bindings_manager.get_vi_state( cli) if cli.current_buffer.read_only(): vi_state.input_mode = InputMode.NAVIGATION else: vi_state.input_mode = InputMode.INSERT application = Application( layout=self.layout_manager.layout, key_bindings_registry=self.key_bindings_manager.registry, buffers=_BufferMapping(self), mouse_support=Condition(lambda cli: self.enable_mouse_support), use_alternate_screen=True, style=self.style, get_title=get_title) cli = CommandLineInterface(application=application, output=output, input=input, eventloop=self.eventloop) # Set render postpone time. (.1 instead of 0). # This small change ensures that if for a split second a process # outputs a lot of information, we don't give the highest priority to # rendering output. (Nobody reads that fast in real-time.) cli.max_render_postpone_time = .1 # Second. # Hide message when a key has been pressed. def key_pressed(): self.get_client_state(cli).message = None cli.input_processor.beforeKeyPress += key_pressed cli._is_running = True self.clis[connection] = cli # Redraw all CLIs. (Adding a new client could mean that the others # change size, so everything has to be redrawn.) self.invalidate() cli.on_invalidate += lambda: self.invalidate() # Handle start-up comands. # (Does initial key bindings.) if not self._startup_done: self._startup_done = True # Execute default config. for cmd in STARTUP_COMMANDS.splitlines(): self.handle_command(cli, cmd) # Source the given file. if self.source_file: call_command_handler('source-file', self, cli, [self.source_file]) # Make sure that there is one window created. self.create_window(cli, command=self.startup_command) return cli
def init_prompt_toolkit_cli(self): if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty(): # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode) insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT) # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws )) def _(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) self._app = create_prompt_application( key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options() ) self.pt_cli = CommandLineInterface(self._app, eventloop=create_eventloop(self.inputhook))
def init_prompt_toolkit_cli(self): if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty(): # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode) insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT) # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws )) def _(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', } if self.highlighting_style: style_cls = get_style_by_name(self.highlighting_style) else: style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) app = create_prompt_application(multiline=True, lexer=IPythonPTLexer(), get_prompt_tokens=self.get_prompt_tokens, get_continuation_tokens=self.get_continuation_tokens, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, reserve_space_for_menu=6, ) self.pt_cli = CommandLineInterface(app, eventloop=create_eventloop(self.inputhook))
class TerminalInteractiveShell(InteractiveShell): colors_force = True pt_cli = None vi_mode = Bool(False, config=True, help="Use vi style keybindings at the prompt", ) mouse_support = Bool(False, config=True, help="Enable mouse support in the prompt" ) highlighting_style = Unicode('', config=True, help="The name of a Pygments style to use for syntax highlighting" ) highlighting_style_overrides = Dict(config=True, help="Override highlighting format for specific tokens" ) editor = Unicode(get_default_editor(), config=True, help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." ) term_title = Bool(True, config=True, help="Automatically set the terminal title" ) def _term_title_changed(self, name, new_value): self.init_term_title() def init_term_title(self): # Enable or disable the terminal title. if self.term_title: toggle_set_term_title(True) set_term_title('IPython: ' + abbrev_cwd()) else: toggle_set_term_title(False) def get_prompt_tokens(self, cli): return [ (Token.Prompt, 'In ['), (Token.PromptNum, str(self.execution_count)), (Token.Prompt, ']: '), ] def get_continuation_tokens(self, cli, width): return [ (Token.Prompt, (' ' * (width - 5)) + '...: '), ] def init_prompt_toolkit_cli(self): if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty(): # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode) insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT) # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws )) def _(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', } if self.highlighting_style: style_cls = get_style_by_name(self.highlighting_style) else: style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) app = create_prompt_application(multiline=True, lexer=IPythonPTLexer(), get_prompt_tokens=self.get_prompt_tokens, get_continuation_tokens=self.get_continuation_tokens, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, reserve_space_for_menu=6, ) self.pt_cli = CommandLineInterface(app, eventloop=create_eventloop(self.inputhook)) def prompt_for_code(self): document = self.pt_cli.run(pre_run=self.pre_prompt) return document.text def init_io(self): if sys.platform not in {'win32', 'cli'}: return import colorama colorama.init() # For some reason we make these wrappers around stdout/stderr. # For now, we need to reset them so all output gets coloured. # https://github.com/ipython/ipython/issues/8669 from IPython.utils import io io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': for cmd in ['clear', 'more', 'less', 'man']: self.alias_manager.soft_define_alias(cmd, cmd) def __init__(self, *args, **kwargs): super(TerminalInteractiveShell, self).__init__(*args, **kwargs) self.init_prompt_toolkit_cli() self.init_term_title() self.keep_running = True def ask_exit(self): self.keep_running = False rl_next_input = None def pre_prompt(self): if self.rl_next_input: self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input) self.rl_next_input = None def interact(self): while self.keep_running: print(self.separate_in, end='') try: code = self.prompt_for_code() except EOFError: if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): self.ask_exit() else: if code: self.run_cell(code, store_history=True) def mainloop(self): # An extra layer of protection in case someone mashing Ctrl-C breaks # out of our internal code. while True: try: self.interact() break except KeyboardInterrupt: print("\nKeyboardInterrupt escaped interact()\n") _inputhook = None def inputhook(self, context): if self._inputhook is not None: self._inputhook(context) def enable_gui(self, gui=None): if gui: self._inputhook = get_inputhook_func(gui) else: self._inputhook = None
def create_interface(self): """ instantiates the intereface """ return CommandLineInterface(application=self.create_application(), eventloop=create_eventloop())
def prompt(self, message='', **kwargs): """Get input from the user and return it. This is a wrapper around a lot of prompt_toolkit functionality and can be a replacement for raw_input. (or GNU readline.) If you want to keep your history across several calls, create one `~prompt_toolkit.history.History instance and pass it every time. This function accepts many keyword arguments. Except for the following. they are a proxy to the arguments of create_prompt_application(). Parameters ---------- patch_stdout : file-like, optional Replace ``sys.stdout`` by a proxy that ensures that print statements from other threads won't destroy the prompt. (They will be printed above the prompt instead.) return_asyncio_coroutine : bool, optional When True, return a asyncio coroutine. (Python >3.3) Notes ----- This method was forked from the mainline prompt-toolkit repo. Copyright (c) 2014, Jonathan Slenders, All rights reserved. """ patch_stdout = kwargs.pop('patch_stdout', False) return_asyncio_coroutine = kwargs.pop('return_asyncio_coroutine', False) if return_asyncio_coroutine: eventloop = create_asyncio_eventloop() else: eventloop = kwargs.pop('eventloop', None) or create_eventloop() # Create CommandLineInterface. if self.cli is None: if builtins.__xonsh_env__.get('VI_MODE'): editing_mode = EditingMode.VI else: editing_mode = EditingMode.EMACS kwargs['editing_mode'] = editing_mode cli = CommandLineInterface(application=create_prompt_application( message, **kwargs), eventloop=eventloop, output=create_output()) self.cli = cli else: cli = self.cli # Replace stdout. patch_context = cli.patch_stdout_context( ) if patch_stdout else DummyContext() # Read input and return it. if return_asyncio_coroutine: # Create an asyncio coroutine and call it. exec_context = {'patch_context': patch_context, 'cli': cli} exec( textwrap.dedent(''' import asyncio @asyncio.coroutine def prompt_coro(): with patch_context: document = yield from cli.run_async(reset_current_buffer=False) if document: return document.text '''), exec_context) return exec_context['prompt_coro']() else: # Note: We pass `reset_current_buffer=False`, because that way # it's easy to give DEFAULT_BUFFER a default value, without it # getting erased. We don't have to reset anyway, because this is # the first and only time that this CommandLineInterface will run. try: with patch_context: document = cli.run(reset_current_buffer=False) if document: return document.text finally: eventloop.close()
def move_cursor_up(event): ic.selected_option_index = ((ic.selected_option_index - 1) % ic.choice_count) @manager.registry.add_binding(Keys.Enter, eager=True) def set_answer(event): ic.answered = True event.cli.set_return_value(None) inquirer_style = style_from_dict({ Token.QuestionMark: '#5F819D', Token.Selected: '#FF9D00', Token.Instruction: '', Token.Answer: '#FF9D00 bold', Token.Question: 'bold', }) app = Application(layout=layout, key_bindings_registry=manager.registry, mouse_support=False, style=inquirer_style) eventloop = create_eventloop() try: cli = CommandLineInterface(application=app, eventloop=eventloop) cli.run(reset_current_buffer=False) finally: eventloop.close()