def magictype(text, prompt_template="default", speed=1): """Echo each character in ``text`` as keyboard characters are pressed. Characters are echo'd ``speed`` characters at a time. """ echo_prompt(prompt_template) cursor_position = 0 with raw_mode(): while True: char = text[cursor_position:cursor_position + speed] in_char = getchar() if in_char in {ESC, CTRLC}: echo(carriage_return=True) raise click.Abort() elif in_char == BACKSPACE: if cursor_position > 0: echo("\b \b", nl=False) cursor_position -= 1 elif in_char in RETURNS: # Only return at end of command if cursor_position >= len(text): echo("\r", nl=True) break elif in_char == CTRLZ and hasattr(signal, "SIGTSTP"): # Background process os.kill(0, signal.SIGTSTP) # When doitlive is back in foreground, clear the terminal # and resume where we left off click.clear() echo_prompt(prompt_template) echo(text[:cursor_position], nl=False) else: if cursor_position < len(text): echo(char, nl=False) increment = min([speed, len(text) - cursor_position]) cursor_position += increment
def run_commands(self): """Automatically type and execute all commands.""" more = 0 prompt = sys.ps1 for command in self.commands: try: prompt = sys.ps2 if more else sys.ps1 try: magictype(command, prompt_template=prompt, speed=self.speed) except EOFError: self.write("\n") break else: if command.strip() == "exit()": return more = self.push(command) except KeyboardInterrupt: self.write("\nKeyboardInterrupt\n") self.resetbuffer() more = 0 sys.exit(1) echo_prompt(prompt) wait_for(RETURNS)
def regulartype(prompt_template="default"): """Echo each character typed. Unlike magictype, this echos the characters the user is pressing. Returns: command_string | The command to be passed to the shell to run. This is | typed by the user. """ echo_prompt(prompt_template) command_string = "" cursor_position = 0 with raw_mode(): while True: in_char = getchar() if in_char in {ESC, CTRLC}: echo(carriage_return=True) raise click.Abort() elif in_char == TAB: echo("\r", nl=True) return in_char elif in_char == BACKSPACE: if cursor_position > 0: echo("\b \b", nl=False) command_string = command_string[:-1] cursor_position -= 1 elif in_char in RETURNS: echo("\r", nl=True) return command_string elif in_char == CTRLZ and hasattr(signal, "SIGTSTP"): # Background process os.kill(0, signal.SIGTSTP) # When doitlive is back in foreground, clear the terminal # and resume where we left off click.clear() echo_prompt(prompt_template) else: echo(in_char, nl=False) command_string += in_char cursor_position += 1
def run( commands, shell=None, prompt_template="default", speed=1, quiet=False, test_mode=False, commentecho=False, ): """Main function for "magic-running" a list of commands.""" if not quiet: secho("We'll do it live!", fg="red", bold=True) secho( "STARTING SESSION: Press Ctrl-C at any time to exit.", fg="yellow", bold=True, ) click.pause() click.clear() state = SessionState( shell=shell, prompt_template=prompt_template, speed=speed, test_mode=test_mode, commentecho=commentecho, ) i = 0 while i < len(commands): command = commands[i].strip() i += 1 if not command: continue is_comment = command.startswith("#") if not is_comment: command_as_list = shlex.split(ensure_utf8(command)) else: command_as_list = None shell_match = SHELL_RE.match(command) if is_comment: # Parse comment magic match = OPTION_RE.match(command) if match: option, arg = match.group("option"), match.group("arg") func = OPTION_MAP[option] func(state, arg) elif state.commentecho(): comment = command.lstrip("#") secho(comment, fg="yellow", bold=True) continue # Handle 'export' and 'alias' commands by storing them in SessionState elif command_as_list and command_as_list[0] in ["alias", "export"]: magictype(command, prompt_template=state["prompt_template"], speed=state["speed"]) # Store the raw commands instead of using add_envvar and add_alias # to avoid having to parse the command ourselves state.add_command(command) # Handle ```python and ```ipython by running "player" consoles elif shell_match: shell_name = shell_match.groups()[0].strip() py_commands = [] more = True while more: # slurp up all the python code try: py_command = commands[i].rstrip() except IndexError: raise SessionError("Unmatched {0} code block in " "session file.".format(shell_name)) i += 1 if py_command.startswith("```"): i += 1 more = False else: py_commands.append(py_command) # Run the player console magictype( shell_name, prompt_template=state["prompt_template"], speed=state["speed"], ) if shell_name == "ipython": try: from doitlive.ipython_consoles import start_ipython_player except ImportError: raise RuntimeError( "```ipython blocks require IPython to be installed") # dedent all the commands to account for IPython's autoindentation ipy_commands = [textwrap.dedent(cmd) for cmd in py_commands] start_ipython_player(ipy_commands, speed=state["speed"]) else: start_python_player(py_commands, speed=state["speed"]) else: # goto_stealthmode determines when to switch to stealthmode goto_stealthmode = magicrun(command, **state) # stealthmode allows user to type live commands outside of automated script i -= stealthmode(state, goto_stealthmode) echo_prompt(state["prompt_template"]) wait_for(RETURNS) if not quiet: secho("FINISHED SESSION", fg="yellow", bold=True)
def run(commands, shell=None, prompt_template='default', speed=1, quiet=False, test_mode=False, commentecho=False): """Main function for "magic-running" a list of commands.""" if not quiet: secho("We'll do it live!", fg='red', bold=True) secho('STARTING SESSION: Press Ctrl-C at any time to exit.', fg='yellow', bold=True) click.pause() click.clear() state = SessionState(shell=shell, prompt_template=prompt_template, speed=speed, test_mode=test_mode, commentecho=commentecho) i = 0 while i < len(commands): command = commands[i].strip() command_as_list = shlex.split(ensure_utf8(command)) i += 1 if not command: continue shell_match = SHELL_RE.match(command) if command.startswith('#'): # Parse comment magic match = OPTION_RE.match(command) if match: option, arg = match.group('option'), match.group('arg') func = OPTION_MAP[option] func(state, arg) elif state.commentecho(): comment = command.lstrip("#") secho(comment, fg='yellow', bold=True) continue # Handle 'export' and 'alias' commands by storing them in SessionState elif command_as_list and command_as_list[0] in ['alias', 'export']: magictype(command, prompt_template=state['prompt_template'], speed=state['speed']) # Store the raw commands instead of using add_envvar and add_alias # to avoid having to parse the command ourselves state.add_command(command) # Handle ```python and ```ipython by running "player" consoles elif shell_match: shell_name = shell_match.groups()[0].strip() py_commands = [] more = True while more: # slurp up all the python code try: py_command = commands[i].rstrip() except IndexError: raise SessionError('Unmatched {0} code block in ' 'session file.'.format(shell_name)) i += 1 if py_command.startswith('```'): i += 1 more = False else: py_commands.append(py_command) # Run the player console magictype(shell_name, prompt_template=state['prompt_template'], speed=state['speed']) if shell_name == 'ipython': try: from doitlive.ipython_consoles import start_ipython_player except ImportError: raise RuntimeError('```ipython blocks require IPython to be installed') # dedent all the commands to account for IPython's autoindentation ipy_commands = [textwrap.dedent(cmd) for cmd in py_commands] start_ipython_player(ipy_commands, speed=state['speed']) else: start_python_player(py_commands, speed=state['speed']) else: magicrun(command, **state) echo_prompt(state['prompt_template']) wait_for(RETURNS) if not quiet: secho("FINISHED SESSION", fg='yellow', bold=True)