def _accept(self): """ Accept new incoming connection. """ conn, addr = self._listen_socket.accept() logger.info('New connection %r %r', *addr) connection = TelnetConnection(conn, addr, self.interact, self, encoding=self.encoding, style=self.style) self.connections.add(connection) # Run application for this connection. def run(): logger.info('Starting interaction %r %r', *addr) try: yield From(connection.run_application()) except Exception as e: print(e) finally: self.connections.remove(connection) logger.info('Stopping interaction %r %r', *addr) ensure_future(run())
def do_finish(*event): ok, show, item_id, job_id, due = dataview.maybe_finish(text_area.document.cursor_position_row) if not ok: return def coroutine(): dialog = TextInputDialog( title='finish task/job', label_text=f"selected: {show}\ndatetime completed:") done_str = yield From(show_dialog_as_float(dialog)) if done_str: try: done = parse_datetime(done_str)[1] except ValueError: show_message('Finish task/job?', 'Invalid finished datetime') else: # valid done item.finish_item(item_id, job_id, done, due) # dataview.itemcache[item.doc_id] = {} if item_id in dataview.itemcache: del dataview.itemcache[item_id] loop = get_event_loop() loop.call_later(0, data_changed, loop) ensure_future(coroutine())
def wait_for_cpr_responses(self, timeout=1): """ Wait for a CPR response. """ cpr_futures = list(self._waiting_for_cpr_futures) # Make copy. # When there are no CPRs in the queue. Don't do anything. if not cpr_futures or self.cpr_support == CPR_Support.NOT_SUPPORTED: return Future.succeed(None) f = Future() # When a CPR has been received, set the result. def wait_for_responses(): for response_f in cpr_futures: yield From(response_f) if not f.done(): f.set_result(None) ensure_future(wait_for_responses()) # Timeout. def wait_for_timeout(): time.sleep(timeout) # Got timeout. if not f.done(): self._waiting_for_cpr_futures = deque() f.set_result(None) t = threading.Thread(target=wait_for_timeout) t.daemon = True t.start() return f
def do_import_file(*event): # TODO: add dialog msg = "" def coroutine(): global msg dialog = TextInputDialog( title='import file', label_text="""\ It is possible to import data from files with one of the following extensions: .json a json file exported from etm 3.2.x .text a text file with etm entries as lines .ics an iCalendar file Enter the path of the file to import:""") file_path = yield From(show_dialog_as_float(dialog)) if file_path: msg = import_file(file_path) if msg: dataview.refreshRelevant() dataview.refreshAgenda() dataview.refreshCurrent() loop = get_event_loop() loop.call_later(0, data_changed, loop) show_message('import file', msg) ensure_future(coroutine())
def do_reschedule(*event): doc_id, instance, job = dataview.get_row_details(text_area.document.cursor_position_row) if not doc_id: return hsh = DBITEM.get(doc_id=doc_id) def coroutine(): dialog = TextInputDialog( title='reschedule instance', label_text=f"selected: {hsh['itemtype']} {hsh['summary']}\ninstance: {format_datetime(instance)[1]}\n\nnew datetime:") new_datetime = yield From(show_dialog_as_float(dialog)) if not new_datetime: return changed = False ok, dt, z = parse_datetime(new_datetime) if ok: changed = item.reschedule(doc_id, instance, dt) else: show_message('new instance', f"'{new_datetime}' is invalid") if changed: if doc_id in dataview.itemcache: del dataview.itemcache[doc_id] application.layout.focus(text_area) set_text(dataview.show_active_view()) loop = get_event_loop() loop.call_later(0, data_changed, loop) ensure_future(coroutine())
def __init__(self, pipe_name, read_callback, done_callback): self.pipe_name = pipe_name self.read_callback = read_callback self.done_callback = done_callback self.done = False self.handle = windll.kernel32.CreateFileW(pipe_name, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None) if self.handle == INVALID_HANDLE_VALUE: error_code = windll.kernel32.GetLastError() raise Exception('Invalid pipe handle. Error code=%r.' % error_code) # Create overlapped structure and event. self._overlapped = OVERLAPPED() self._event = windll.kernel32.CreateEventA( None, # Default security attributes. BOOL(True), # Manual reset event. BOOL(True), # initial state = signaled. None # Unnamed event object. ) self._overlapped.hEvent = self._event self._reading = Event() # Start reader coroutine. ensure_future(self._async_reader())
def _(event): # match R behavior editor = roption("editor") if not editor or not isinstance(editor, text_type): if 'VISUAL' in os.environ: editor = os.environ['VISUAL'] elif 'EDITOR' in os.environ: editor = os.environ['EDITOR'] if not editor: editor = "vi" buff = event.current_buffer if editor: orig_visual = os.environ[ 'VISUAL'] if 'VISUAL' in os.environ else None os.environ['VISUAL'] = editor buff.open_in_editor() if editor: def run(): def cleanup(): if orig_visual: os.environ['VISUAL'] = orig_visual else: del os.environ['VISUAL'] yield From(run_in_terminal(cleanup, in_executor=True)) ensure_future(run())
def do_maybe_cancel_timer(*event): if not dataview.timer_id: return item_id = dataview.timer_id job_id = dataview.timer_job hsh = DBITEM.get(doc_id=item_id) item_info = f"{hsh['itemtype']} {hsh['summary']}" stopped_timer = False now = pendulum.now() if dataview.timer_status == 1: #running time = dataview.timer_time + (now - dataview.timer_start) else: time = dataview.timer_time completed = pendulum.now() completed_str = format_datetime(completed) time_str = format_duration(time) def coroutine(): dialog = ConfirmDialog("cancel timer", f"item: {item_info}\nelapsed time: {time_str}\n\nclose timer without recording?") record_cancel = yield From(show_dialog_as_float(dialog)) if record_cancel: dataview.timer_clear() set_text(dataview.show_active_view()) get_app().invalidate() ensure_future(coroutine())
def do_maybe_record_timer(*event): if not dataview.timer_id: return item_id = dataview.timer_id job_id = dataview.timer_job hsh = DBITEM.get(doc_id=item_id) item_info = f"{hsh['itemtype']} {hsh['summary']}" now = pendulum.now() if dataview.timer_status == 1: #running time = dataview.timer_time + (now - dataview.timer_start) else: time = dataview.timer_time completed = pendulum.now() completed_str = format_datetime(completed) time_str = format_duration(time) def coroutine(): dialog = ConfirmDialog("record time", f"item: {item_info}\nelapsed time: {time_str}\n\nrecord time and close timer?") record_close = yield From(show_dialog_as_float(dialog)) if record_close: item.record_timer(item_id, job_id, completed, time) set_text(dataview.show_active_view()) dataview.timer_clear() if item_id in dataview.itemcache: del dataview.itemcache[item_id] loop = get_event_loop() loop.call_later(0, data_changed, loop) ensure_future(coroutine())
def handler(): def coroutine(): listening_dialog = ListeningDialog(mic, recognizer) audio = yield From(show_dialog(listening_dialog)) tokens = recognize(vl_stub, vl_metadata, audio) possible_commands = home.match_command(tokens) text_area.text = ' '.join(tokens) text_area.text = str(home) if not possible_commands: err_msg = 'Unrecognized command: {}'.format(' '.join(tokens)) err_msg_dialog = MessageDialog(err_msg, 'Error') yield From(show_dialog(err_msg_dialog)) elif len(possible_commands) == 1 and possible_commands[0][0] == 0: [(_, _, action)] = possible_commands action() else: choice_dialog = ChoiceDialog( [cmd for _, cmd, _ in possible_commands[:3]]) choice = yield From(show_dialog(choice_dialog)) if choice is not None: _, _, action = possible_commands[choice] action() text_area.text = str(home) ensure_future(coroutine())
def run_coroutine_in_terminal(async_func, render_cli_done=False): """ Suspend the current application and run this coroutine instead. `async_func` can be a coroutine or a function that returns a Future. :param async_func: A function that returns either a Future or coroutine when called. :returns: A `Future`. """ assert callable(async_func) loop = get_event_loop() # Make sure to run this function in the current `Application`, or if no # application is active, run it normally. app = get_app(return_none=True) if app is None: return ensure_future(async_func()) assert app._is_running # When a previous `run_in_terminal` call was in progress. Wait for that # to finish, before starting this one. Chain to previous call. previous_run_in_terminal_f = app._running_in_terminal_f new_run_in_terminal_f = loop.create_future() app._running_in_terminal_f = new_run_in_terminal_f def _run_in_t(): " Coroutine. " # Wait for the previous `run_in_terminal` to finish. if previous_run_in_terminal_f is not None: yield previous_run_in_terminal_f # Draw interface in 'done' state, or erase. if render_cli_done: app._redraw(render_as_done=True) else: app.renderer.erase() # Disable rendering. app._running_in_terminal = True # Detach input. try: with app.input.detach(): with app.input.cooked_mode(): result = yield From(async_func()) raise Return(result) # Same as: "return result" finally: # Redraw interface again. try: app._running_in_terminal = False app.renderer.reset() app._request_absolute_cursor_position() app._redraw() finally: new_run_in_terminal_f.set_result(None) return ensure_future(_run_in_t())
def do_maybe_delete(*event): doc_id, instance, job = dataview.get_row_details(text_area.document.cursor_position_row) if not doc_id: return hsh = DBITEM.get(doc_id=doc_id) if not instance: # not repeating def coroutine(): dialog = ConfirmDialog("Delete", f"Selected: {hsh['itemtype']} {hsh['summary']}\n\nAre you sure you want to delete this item?\nThis would remove the item from the database\nand cannot be undone.") delete = yield From(show_dialog_as_float(dialog)) if delete: item.delete_item(doc_id) if doc_id in dataview.itemcache: del dataview.itemcache[doc_id] application.layout.focus(text_area) set_text(dataview.show_active_view()) loop = get_event_loop() loop.call_later(0, data_changed, loop) ensure_future(coroutine()) if instance: # repeating def coroutine(): # radios.current_value will contain the first component of the selected tuple title = "Delete" text = f"Selected: {hsh['itemtype']} {hsh['summary']}\nInstance: {format_datetime(instance)[1]}\n\nDelete what?" values =[ (0, 'just this instance'), (1, 'the item itself'), ] dialog = RadioListDialog( title=title, text=text, values=values) which = yield From(show_dialog_as_float(dialog)) if which is not None: changed = item.delete_instances(doc_id, instance, which) if changed: if doc_id in dataview.itemcache: del dataview.itemcache[doc_id] application.layout.focus(text_area) set_text(dataview.show_active_view()) loop = get_event_loop() loop.call_later(0, data_changed, loop) ensure_future(coroutine())
def do_go_to_date(*event): def coroutine(): dialog = TextInputDialog( title='Go to date', label_text='date:') target_date = yield From(show_dialog_as_float(dialog)) try: dataview.dtYrWk(target_date) except ValueError: show_message('go to date', 'Invalid date') else: set_text(dataview.show_active_view()) ensure_future(coroutine())
def do_go_to(): def coroutine(): dialog = TextInputDialog(title='Go to line', label_text='Line number:') line_number = yield From(show_dialog_as_float(dialog)) try: line_number = int(line_number) except ValueError: show_message('Invalid line number') else: text_field.buffer.cursor_position = \ text_field.buffer.document.translate_row_col_to_index(line_number - 1, 0) ensure_future(coroutine())
def bind_and_listen_on_win32_socket(socket_name, accept_callback): """ :param accept_callback: Called with `Win32PipeConnection` when a new connection is established. """ assert callable(accept_callback) socket_name = r'\\.\pipe\pymux.sock.jonathan.42' pipes = [PipeInstance(socket_name, pipe_connection_cb=accept_callback) for i in range(INSTANCES)] for p in pipes: # Start pipe. ensure_future(p.handle_pipe()) return socket_name
def main(): loop = get_event_loop() f = ensure_future(my_coroutine()) # Run the event loop, until the coroutine is done. loop.run_until_complete(f) print(f.result())
def save_before_quit(*event): def coroutine(): dialog = ConfirmDialog("unsaved changes", "Save them before closing?") save_changes = yield From(show_dialog_as_float(dialog)) if save_changes: if item.doc_id is not None: # del dataview.itemcache[item.doc_id] dataview.itemcache[item.doc_id] = {} dataview.is_editing = False application.layout.focus(text_area) set_text(dataview.show_active_view()) loop = get_event_loop() loop.call_later(0, item_changed, loop) ensure_future(coroutine())
def _send_packet(self, data): """ Send packet to client. """ if self._closed: return data = json.dumps(data) def send(): try: yield From(self.pipe_connection.write(data)) except BrokenPipeError: self.detach_and_close() ensure_future(send())
def do_go_to(): def coroutine(): dialog = TextInputDialog( title='Go to line', label_text='Line number:') line_number = yield From(show_dialog_as_float(dialog)) try: line_number = int(line_number) except ValueError: show_message('Invalid line number') else: text_field.buffer.cursor_position = \ text_field.buffer.document.translate_row_col_to_index(line_number - 1, 0) ensure_future(coroutine())
def bind_and_listen_on_win32_socket(socket_name, accept_callback): """ :param accept_callback: Called with `Win32PipeConnection` when a new connection is established. """ assert callable(accept_callback) socket_name = r'\\.\pipe\pymux.sock.jonathan.42' pipes = [ PipeInstance(socket_name, pipe_connection_cb=accept_callback) for i in range(INSTANCES) ] for p in pipes: # Start pipe. ensure_future(p.handle_pipe()) return socket_name
def do_open_file(): def coroutine(): global current_path open_dialog = TextInputDialog(title='Open file', label_text='Enter the path of a file:', completer=PathCompleter()) path = yield From(show_dialog_as_float(open_dialog)) current_path = path if path is not None: try: with open(path, 'rb') as f: text_field.text = f.read().decode('utf-8', errors='ignore') except IOError as e: show_message('Error', '{}'.format(e)) ensure_future(coroutine())
def do_open_file(): def coroutine(): global current_path open_dialog = TextInputDialog( title='Open file', label_text='Enter the path of a file:', completer=PathCompleter()) path = yield From(show_dialog_as_float(open_dialog)) current_path = path if path is not None: try: with open(path, 'rb') as f: text_field.text = f.read().decode('utf-8', errors='ignore') except IOError as e: show_message('Error', '{}'.format(e)) ensure_future(coroutine())
def message_dialog(title='', text='', ok_text='Ok', width=None, wrap_lines=True, scrollbar=None): """ Display a simple message box and wait until the user presses enter. """ def coroutine(): dialog = MessageDialog(title=title, text=text, ok_text=ok_text, width=width, wrap_lines=wrap_lines, scrollbar=scrollbar) yield From(show_dialog(dialog)) ensure_future(coroutine())
def __init__(self, pymux, pipe_connection): self.pymux = pymux self.pipe_connection = pipe_connection self.size = Size(rows=20, columns=80) self._closed = False self._recv_buffer = b'' self.client_state = None def feed_key(key): self.client_state.app.key_processor.feed(key) self.client_state.app.key_processor.process_keys() self._inputstream = Vt100Parser(feed_key) self._pipeinput = _ClientInput(self._send_packet) ensure_future(self._start_reading())
def input_dialog(title='', text='', ok_text='OK', cancel_text='Cancel', completer=None, password=False): """ Display a text input box. Return the given text, or None when cancelled. """ output_text = '' def coroutine(): global output_text open_dialog = TextInputDialog(title, text, completer) output_text = yield From(show_dialog(open_dialog)) ensure_future(coroutine()) return output_text
def datetime_calculator(*event): def coroutine(): prompt = """\ Enter an expression of the form: x [+-] y where x is a datetime and y is either [+] a timeperiod [-] a datetime or a timeperiod Be sure to surround [+-] with spaces. Timezones can be appended to x and y. """ dialog = InteractiveInputDialog( title='datetime calculator', help_text=prompt, evaluator=datetime_calculator, padding=4, ) yield From(show_dialog_as_float(dialog)) ensure_future(coroutine())
def do_go_to_line(*event): def coroutine(): default = '' if dataview.current_row: default = dataview.current_row + 1 dialog = TextInputDialog( title='Go to line', label_text='Line number:', default=str(default)) line_number = yield From(show_dialog_as_float(dialog)) if line_number: try: line_number = int(line_number) except ValueError: show_message('go to line', 'Invalid line number') else: text_area.buffer.cursor_position = \ text_area.buffer.document.translate_row_col_to_index(line_number - 1, 0) ensure_future(coroutine())
def test_empty_async_generator(): " Test asynchronous generator. " items = [] f = ensure_future(consume_async_generator( _empty_async_generator(), lambda: False, items.append)) # Run the event loop until all items are collected. get_event_loop().run_until_complete(f) assert items == [] # Check that `consume_async_generator` didn't fail. assert f.result() is None
def yes_no_dialog(title='', text='', yes_text='Yes', yes_func=func_pass, no_text='No', no_func=func_pass): """ Display a Yes/No dialog. Execute a passed function. """ def coroutine(): dialog = YesNoDialog(title=title, text=text, yes_text=yes_text, no_text=no_text) result = yield From(show_dialog(dialog)) if result == True: yes_func() else: no_func() ensure_future(coroutine())
def run_coroutine_in_terminal(self, async_func, render_cli_done=False): """ `async_func` can be a coroutine or a function that returns a Future. :param async_func: A function that returns either a Future or coroutine when called. :returns: A `Future`. """ assert callable(async_func) loop = get_event_loop() # When a previous `run_in_terminal` call was in progress. Wait for that # to finish, before starting this one. Chain to previous call. previous_run_in_terminal_f = self._running_in_terminal_f new_run_in_terminal_f = loop.create_future() self._running_in_terminal_f = new_run_in_terminal_f def _run_in_t(): " Coroutine. " # Wait for the previous `run_in_terminal` to finish. if previous_run_in_terminal_f is not None: yield previous_run_in_terminal_f # Draw interface in 'done' state, or erase. if render_cli_done: self._redraw(render_as_done=True) else: self.renderer.erase() # Disable rendering. self._running_in_terminal = True # Detach input. try: with self.input.detach(): with self.input.cooked_mode(): result = yield From(async_func()) raise Return(result) # Same as: "return result" finally: # Redraw interface again. try: self._running_in_terminal = False self.renderer.reset() self._request_absolute_cursor_position() self._redraw() finally: new_run_in_terminal_f.set_result(None) return ensure_future(_run_in_t())
def attach(self, detach_other_clients=False, color_depth=ColorDepth.DEPTH_8_BIT): assert isinstance(detach_other_clients, bool) self._send_size() self._send_packet({ 'cmd': 'start-gui', 'detach-others': detach_other_clients, 'color-depth': color_depth, 'term': os.environ.get('TERM', ''), 'data': '' }) f = ensure_future(self._start_reader()) with self._input.attach(self._input_ready): # Run as long as we have a connection with the server. get_event_loop().run_until_complete(f) # Run forever.
def test_generator_to_async_generator(): """ Test conversion of sync to asycn generator. This should run the synchronous parts in a background thread. """ async_gen = generator_to_async_generator(_sync_generator) items = [] f = ensure_future(consume_async_generator( async_gen, lambda: False, items.append)) # Run the event loop until all items are collected. get_event_loop().run_until_complete(f) assert items == [1, 10] # Check that `consume_async_generator` didn't fail. assert f.result() is None
def run_application(self): """ Run application. """ def handle_incoming_data(): data = self.conn.recv(1024) if data: self.feed(data) else: # Connection closed by client. logger.info('Connection closed by client. %r %r' % self.addr) self.close() raise Exception("Dirty connection close.") def run(): with context() as ctx_id: self._context_id = ctx_id # Set input/output for all application running in this context. set_default_input(self.vt100_input) set_default_output(self.vt100_output) # Add reader. loop = get_event_loop() loop.add_reader(self.conn, handle_incoming_data) try: obj = self.interact(self) if _is_coroutine(obj): # Got an asyncio coroutine. import asyncio f = asyncio.ensure_future(obj) yield From(Future.from_asyncio_future(f)) else: # Got a prompt_toolkit coroutine. yield From(obj) except Exception as e: print('Got %s' % type(e).__name__, e) import traceback traceback.print_exc() raise finally: self.close() return ensure_future(run())
def prompt( self, # When any of these arguments are passed, this value is overwritten # in this PromptSession. message=None, # `message` should go first, because people call it # as positional argument. editing_mode=None, refresh_interval=None, vi_mode=None, lexer=None, completer=None, complete_in_thread=None, is_password=None, key_bindings=None, bottom_toolbar=None, style=None, color_depth=None, include_default_pygments_style=None, style_transformation=None, swap_light_and_dark_colors=None, rprompt=None, multiline=None, prompt_continuation=None, wrap_lines=None, enable_history_search=None, search_ignore_case=None, complete_while_typing=None, validate_while_typing=None, complete_style=None, auto_suggest=None, validator=None, clipboard=None, mouse_support=None, input_processors=None, reserve_space_for_menu=None, enable_system_prompt=None, enable_suspend=None, enable_open_in_editor=None, tempfile_suffix=None, inputhook=None, # Following arguments are specific to the current `prompt()` call. async_=False, default='', accept_default=False, pre_run=None): """ Display the prompt. All the arguments are a subset of the :class:`~.PromptSession` class itself. This will raise ``KeyboardInterrupt`` when control-c has been pressed (for abort) and ``EOFError`` when control-d has been pressed (for exit). Additional arguments, specific for this prompt: :param async_: When `True` return a `Future` instead of waiting for the prompt to finish. :param default: The default input text to be shown. (This can be edited by the user). :param accept_default: When `True`, automatically accept the default value without allowing the user to edit the input. :param pre_run: Callable, called at the start of `Application.run`. """ assert isinstance(default, text_type) # NOTE: We used to create a backup of the PromptSession attributes and # restore them after exiting the prompt. This code has been # removed, because it was confusing and didn't really serve a use # case. (People were changing `Application.editing_mode` # dynamically and surprised that it was reset after every call.) # Take settings from 'prompt'-arguments. for name in self._fields: value = locals()[name] if value is not None: setattr(self, name, value) if vi_mode: self.editing_mode = EditingMode.VI def pre_run2(): if pre_run: pre_run() if accept_default: # Validate and handle input. We use `call_from_executor` in # order to run it "soon" (during the next iteration of the # event loop), instead of right now. Otherwise, it won't # display the default value. get_event_loop().call_from_executor( self.default_buffer.validate_and_handle) def run_sync(): with self._auto_refresh_context(): self.default_buffer.reset(Document(default)) return self.app.run(inputhook=self.inputhook, pre_run=pre_run2) def run_async(): with self._auto_refresh_context(): self.default_buffer.reset(Document(default)) result = yield From(self.app.run_async(pre_run=pre_run2)) raise Return(result) if async_: return ensure_future(run_async()) else: return run_sync()
def prompt( self, message=None, # When any of these arguments are passed, this value is overwritten # for the current prompt. default='', editing_mode=None, refresh_interval=None, vi_mode=None, lexer=None, completer=None, is_password=None, extra_key_bindings=None, bottom_toolbar=None, style=None, rprompt=None, multiline=None, prompt_continuation=None, wrap_lines=None, history=None, enable_history_search=None, complete_while_typing=None, validate_while_typing=None, complete_style=None, auto_suggest=None, validator=None, clipboard=None, mouse_support=None, extra_input_processor=None, reserve_space_for_menu=None, enable_system_prompt=None, enable_suspend=None, enable_open_in_editor=None, tempfile_suffix=None, async_=False): """ Display the prompt. :param _async: When `True` return a `Future` instead of waiting for the prompt to finish. """ # Backup original settings. backup = dict((name, getattr(self, name)) for name in self._fields) # Take settings from 'prompt'-arguments. for name in self._fields: value = locals()[name] if value is not None: setattr(self, name, value) if vi_mode: self.editing_mode = EditingMode.VI def restore(): " Restore original settings. " for name in self._fields: setattr(self, name, backup[name]) def run_sync(): with self._auto_refresh_context(): try: self._default_buffer.reset(Document(self.default)) return self.app.run() finally: restore() def run_async(): with self._auto_refresh_context(): try: self._default_buffer.reset(Document(self.default)) result = yield From(self.app.run_async()) raise Return(result) finally: restore() if async_: return ensure_future(run_async()) else: return run_sync()
def show_message(title, text): def coroutine(): dialog = MessageDialog(title, text) yield From(show_dialog_as_float(dialog)) ensure_future(coroutine())
def help_handler(): def coroutine(): dialog = MessageDialog(HELP, 'HELP') yield From(show_dialog(dialog)) ensure_future(coroutine())
def prompt( self, message=None, # When any of these arguments are passed, this value is overwritten # for the current prompt. default='', editing_mode=None, refresh_interval=None, vi_mode=None, lexer=None, completer=None, complete_in_thread=None, is_password=None, key_bindings=None, bottom_toolbar=None, style=None, color_depth=None, include_default_pygments_style=None, rprompt=None, multiline=None, prompt_continuation=None, wrap_lines=None, enable_history_search=None, search_ignore_case=None, complete_while_typing=None, validate_while_typing=None, complete_style=None, auto_suggest=None, validator=None, clipboard=None, mouse_support=None, input_processors=None, reserve_space_for_menu=None, enable_system_prompt=None, enable_suspend=None, enable_open_in_editor=None, tempfile_suffix=None, inputhook=None, async_=False): """ Display the prompt. All the arguments are a subset of the :class:`~.PromptSession` class itself. This will raise ``KeyboardInterrupt`` when control-c has been pressed (for abort) and ``EOFError`` when control-d has been pressed (for exit). :param async_: When `True` return a `Future` instead of waiting for the prompt to finish. """ # Backup original settings. backup = dict((name, getattr(self, name)) for name in self._fields) # Take settings from 'prompt'-arguments. for name in self._fields: value = locals()[name] if value is not None: setattr(self, name, value) if vi_mode: self.editing_mode = EditingMode.VI def restore(): " Restore original settings. " for name in self._fields: setattr(self, name, backup[name]) def run_sync(): with self._auto_refresh_context(): try: self.default_buffer.reset(Document(self.default)) return self.app.run(inputhook=self.inputhook) finally: restore() def run_async(): with self._auto_refresh_context(): try: self.default_buffer.reset(Document(self.default)) result = yield From(self.app.run_async()) raise Return(result) finally: restore() if async_: return ensure_future(run_async()) else: return run_sync()
def run_async(self, pre_run=None): """ Run asynchronous. Return a prompt_toolkit :class:`~prompt_toolkit.eventloop.Future` object. If you wish to run on top of asyncio, remember that a prompt_toolkit `Future` needs to be converted to an asyncio `Future`. The cleanest way is to call :meth:`~prompt_toolkit.eventloop.Future.to_asyncio_future`. Also make sure to tell prompt_toolkit to use the asyncio event loop. .. code:: python from prompt_toolkit.eventloop import use_asyncio_event_loop from asyncio import get_event_loop use_asyncio_event_loop() get_event_loop().run_until_complete( application.run_async().to_asyncio_future()) """ assert not self._is_running, 'Application is already running.' def _run_async(): " Coroutine. " loop = get_event_loop() f = loop.create_future() self.future = f # XXX: make sure to set this before calling '_redraw'. # Counter for cancelling 'flush' timeouts. Every time when a key is # pressed, we start a 'flush' timer for flushing our escape key. But # when any subsequent input is received, a new timer is started and # the current timer will be ignored. flush_counter = [0] # Non local. # Reset. self.reset() self._pre_run(pre_run) # Feed type ahead input first. self.key_processor.feed_multiple(get_typeahead(self.input)) self.key_processor.process_keys() def read_from_input(): # Ignore when we aren't running anymore. This callback will # removed from the loop next time. (It could be that it was # still in the 'tasks' list of the loop.) # Except: if we need to process incoming CPRs. if not self._is_running and not self.renderer.waiting_for_cpr: return # Get keys from the input object. keys = self.input.read_keys() # Feed to key processor. self.key_processor.feed_multiple(keys) self.key_processor.process_keys() # Quit when the input stream was closed. if self.input.closed: f.set_exception(EOFError) else: # Increase this flush counter. flush_counter[0] += 1 counter = flush_counter[0] # Automatically flush keys. # (_daemon needs to be set, otherwise, this will hang the # application for .5 seconds before exiting.) run_in_executor( lambda: auto_flush_input(counter), _daemon=True) def auto_flush_input(counter): # Flush input after timeout. # (Used for flushing the enter key.) time.sleep(self.ttimeoutlen) if flush_counter[0] == counter: call_from_executor(flush_input) def flush_input(): if not self.is_done: # Get keys, and feed to key processor. keys = self.input.flush_keys() self.key_processor.feed_multiple(keys) self.key_processor.process_keys() if self.input.closed: f.set_exception(EOFError) # Enter raw mode. with self.input.raw_mode(): with self.input.attach(read_from_input): # Draw UI. self._request_absolute_cursor_position() self._redraw() has_sigwinch = hasattr(signal, 'SIGWINCH') and in_main_thread() if has_sigwinch: previous_winch_handler = loop.add_signal_handler( signal.SIGWINCH, self._on_resize) # Wait for UI to finish. try: result = yield From(f) finally: # In any case, when the application finishes. (Successful, # or because of an error.) try: self._redraw(render_as_done=True) finally: # _redraw has a good chance to fail if it calls widgets # with bad code. Make sure to reset the renderer anyway. self.renderer.reset() # Unset `is_running`, this ensures that possibly # scheduled draws won't paint during the following # yield. self._is_running = False # Detach event handlers for invalidate events. # (Important when a UIControl is embedded in # multiple applications, like ptterm in pymux. An # invalidate should not trigger a repaint in # terminated applications.) for ev in self._invalidate_events: ev -= self._invalidate_handler self._invalidate_events = [] # Wait for CPR responses. if self.input.responds_to_cpr: yield From(self.renderer.wait_for_cpr_responses()) if has_sigwinch: loop.add_signal_handler(signal.SIGWINCH, previous_winch_handler) # Wait for the run-in-terminals to terminate. previous_run_in_terminal_f = self._running_in_terminal_f if previous_run_in_terminal_f: yield From(previous_run_in_terminal_f) # Store unprocessed input as typeahead for next time. store_typeahead(self.input, self.key_processor.empty_queue()) raise Return(result) def _run_async2(): self._is_running = True with set_app(self): try: f = From(_run_async()) result = yield f finally: # Set the `_is_running` flag to `False`. Normally this # happened already in the finally block in `run_async` # above, but in case of exceptions, that's not always the # case. self._is_running = False raise Return(result) return ensure_future(_run_async2())
def _send_packet(self, data): " Send to server. " data = json.dumps(data) ensure_future(self.pipe.write_message(data))
def run_async(self, pre_run=None): """ Run asynchronous. Return a prompt_toolkit :class:`~prompt_toolkit.eventloop.Future` object. If you wish to run on top of asyncio, remember that a prompt_toolkit `Future` needs to be converted to an asyncio `Future`. The cleanest way is to call :meth:`~prompt_toolkit.eventloop.Future.to_asyncio_future`. Also make sure to tell prompt_toolkit to use the asyncio event loop. .. code:: python from prompt_toolkit.eventloop import use_asyncio_event_loop from asyncio import get_event_loop use_asyncio_event_loop() get_event_loop().run_until_complete( application.run_async().to_asyncio_future()) """ assert not self._is_running def _run_async(): " Coroutine. " loop = get_event_loop() f = loop.create_future() self.future = f # XXX: make sure to set this before calling '_redraw'. # Counter for cancelling 'flush' timeouts. Every time when a key is # pressed, we start a 'flush' timer for flushing our escape key. But # when any subsequent input is received, a new timer is started and # the current timer will be ignored. flush_counter = [0] # Non local. # Reset. self.reset() self._pre_run(pre_run) # Feed type ahead input first. self.key_processor.feed_multiple(get_typeahead(self.input)) self.key_processor.process_keys() def read_from_input(): # Ignore when we aren't running anymore. This callback will # removed from the loop next time. (It could be that it was # still in the 'tasks' list of the loop.) # Except: if we need to process incoming CPRs. if not self._is_running and not self.renderer.waiting_for_cpr: return # Get keys from the input object. keys = self.input.read_keys() # Feed to key processor. self.key_processor.feed_multiple(keys) self.key_processor.process_keys() # Quit when the input stream was closed. if self.input.closed: f.set_exception(EOFError) else: # Increase this flush counter. flush_counter[0] += 1 counter = flush_counter[0] # Automatically flush keys. # (_daemon needs to be set, otherwise, this will hang the # application for .5 seconds before exiting.) run_in_executor( lambda: auto_flush_input(counter), _daemon=True) def auto_flush_input(counter): # Flush input after timeout. # (Used for flushing the enter key.) time.sleep(self.ttimeoutlen) if flush_counter[0] == counter: call_from_executor(flush_input) def flush_input(): if not self.is_done: # Get keys, and feed to key processor. keys = self.input.flush_keys() self.key_processor.feed_multiple(keys) self.key_processor.process_keys() if self.input.closed: f.set_exception(EOFError) # Enter raw mode. with self.input.raw_mode(): with self.input.attach(read_from_input): # Draw UI. self._request_absolute_cursor_position() self._redraw() has_sigwinch = hasattr(signal, 'SIGWINCH') and in_main_thread() if has_sigwinch: previous_winch_handler = loop.add_signal_handler( signal.SIGWINCH, self._on_resize) # Wait for UI to finish. try: result = yield From(f) finally: # In any case, when the application finishes. (Successful, # or because of an error.) try: self._redraw(render_as_done=True) finally: # _redraw has a good chance to fail if it calls widgets # with bad code. Make sure to reset the renderer anyway. self.renderer.reset() # Unset `is_running`, this ensures that possibly # scheduled draws won't paint during the following # yield. self._is_running = False # Detach event handlers for invalidate events. # (Important when a UIControl is embedded in # multiple applications, like ptterm in pymux. An # invalidate should not trigger a repaint in # terminated applications.) for ev in self._invalidate_events: ev -= self.invalidate self._invalidate_events = [] # Wait for CPR responses. if self.input.responds_to_cpr: yield From(self.renderer.wait_for_cpr_responses()) if has_sigwinch: loop.add_signal_handler(signal.SIGWINCH, previous_winch_handler) # Wait for the run-in-terminals to terminate. previous_run_in_terminal_f = self._running_in_terminal_f if previous_run_in_terminal_f: yield From(previous_run_in_terminal_f) # Store unprocessed input as typeahead for next time. store_typeahead(self.input, self.key_processor.empty_queue()) raise Return(result) def _run_async2(): self._is_running = True with set_app(self): try: f = From(_run_async()) result = yield f finally: assert not self._is_running raise Return(result) return ensure_future(_run_async2())
def run_coroutine_in_terminal(async_func, render_cli_done=False): """ Suspend the current application and run this coroutine instead. `async_func` can be a coroutine or a function that returns a Future. :param async_func: A function that returns either a Future or coroutine when called. :returns: A `Future`. """ assert callable(async_func) loop = get_event_loop() # Make sure to run this function in the current `Application`, or if no # application is active, run it normally. app = get_app(return_none=True) if app is None: return ensure_future(async_func()) assert app._is_running # When a previous `run_in_terminal` call was in progress. Wait for that # to finish, before starting this one. Chain to previous call. previous_run_in_terminal_f = app._running_in_terminal_f new_run_in_terminal_f = loop.create_future() app._running_in_terminal_f = new_run_in_terminal_f def _run_in_t(): " Coroutine. " # Wait for the previous `run_in_terminal` to finish. if previous_run_in_terminal_f is not None: yield previous_run_in_terminal_f # Wait for all CPRs to arrive. We don't want to detach the input until # all cursor position responses have been arrived. Otherwise, the tty # will echo its input and can show stuff like ^[[39;1R. if app.input.responds_to_cpr: yield From(app.renderer.wait_for_cpr_responses()) # Draw interface in 'done' state, or erase. if render_cli_done: app._redraw(render_as_done=True) else: app.renderer.erase() # Disable rendering. app._running_in_terminal = True # Detach input. try: with app.input.detach(): with app.input.cooked_mode(): result = yield From(async_func()) raise Return(result) # Same as: "return result" finally: # Redraw interface again. try: app._running_in_terminal = False app.renderer.reset() app._request_absolute_cursor_position() app._redraw() finally: new_run_in_terminal_f.set_result(None) return ensure_future(_run_in_t())
def prompt( self, message=None, # When any of these arguments are passed, this value is overwritten # for the current prompt. default='', editing_mode=None, refresh_interval=None, vi_mode=None, lexer=None, completer=None, complete_in_thread=None, is_password=None, key_bindings=None, bottom_toolbar=None, style=None, color_depth=None, include_default_pygments_style=None, style_transformation=None, swap_light_and_dark_colors=None, rprompt=None, multiline=None, prompt_continuation=None, wrap_lines=None, enable_history_search=None, search_ignore_case=None, complete_while_typing=None, validate_while_typing=None, complete_style=None, auto_suggest=None, validator=None, clipboard=None, mouse_support=None, input_processors=None, reserve_space_for_menu=None, enable_system_prompt=None, enable_suspend=None, enable_open_in_editor=None, tempfile_suffix=None, inputhook=None, async_=False, accept_default=False, pre_run=None): """ Display the prompt. All the arguments are a subset of the :class:`~.PromptSession` class itself. This will raise ``KeyboardInterrupt`` when control-c has been pressed (for abort) and ``EOFError`` when control-d has been pressed (for exit). :param async_: When `True` return a `Future` instead of waiting for the prompt to finish. :param accept_default: When `True`, automatically accept the default value without allowing the user to edit the input. :param pre_run: Callable, called at the start of `Application.run`. """ # NOTE: We used to create a backup of the PromptSession attributes and # restore them after exiting the prompt. This code has been # removed, because it was confusing and didn't really serve a use # case. (People were changing `Application.editing_mode` # dynamically and surprised that it was reset after every call.) # Take settings from 'prompt'-arguments. for name in self._fields: value = locals()[name] if value is not None: setattr(self, name, value) if vi_mode: self.editing_mode = EditingMode.VI def pre_run2(): if pre_run: pre_run() if accept_default: # Validate and handle input. We use `call_from_executor` in # order to run it "soon" (during the next iteration of the # event loop), instead of right now. Otherwise, it won't # display the default value. get_event_loop().call_from_executor( self.default_buffer.validate_and_handle) def run_sync(): with self._auto_refresh_context(): self.default_buffer.reset(Document(self.default)) return self.app.run(inputhook=self.inputhook, pre_run=pre_run2) def run_async(): with self._auto_refresh_context(): self.default_buffer.reset(Document(self.default)) result = yield From(self.app.run_async(pre_run=pre_run2)) raise Return(result) if async_: return ensure_future(run_async()) else: return run_sync()