def test_IPython(): ipython = get_ipython_cmd() SMM = b'\x1b[?1034h' commands = b"""\ 1 raise Exception undefined def f(): pass f() """ # First the control (without iterm2_tools) p = subprocess.Popen(ipython + ['--quick', '--colors=NoColor', '--no-banner'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate(input=commands) # Different versions of readline do different things with the smm code. stdout = stdout.replace(SMM, b'').strip() assert stdout == b"""\ In [1]: Out[1]: 1 In [2]: \n\ In [2]: --------------------------------------------------------------------------- Exception Traceback (most recent call last) <ipython-input-2-fca2ab0ca76b> in <module>() ----> 1 raise Exception Exception: \n\nIn [3]: --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-3-002bcaa7be0e> in <module>() ----> 1 undefined NameError: name 'undefined' is not defined In [4]: ...: ...: \n\ In [5]: \n\ In [6]: \n\ In [6]: \n\ Do you really want to exit ([y]/n)?\ """ assert stderr == b'' # Now the same thing with iterm2_tools.ipython p = subprocess.Popen(ipython + ['--quick', '--colors=NoColor', '--no-banner', '--ext=iterm2_tools.ipython'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) # Things of note here: # - There are 8 prompts (3 with no input, the second, sixth, and # seventh). The sixth is empty because f() returns None, and the eighth # is not empty because of the exit confirmation. # - The color codes are outside of the iterm2 codes. This is because of # the way IPython handles color codes. See the note in ipython.py. # - The D code (after_output) should always go right before the A code # (before_prompt). # - The fourth and fifth D code (after_output), corresponding to the third # and fourth prompt, should have D;1 (exceptions). The rest should have # D;0. # - The A, B, and D codes should be surrounded by \001 and \002 (C, # before_output) does not need it because it is not in the prompt. stdout, stderr = p.communicate(input=commands) # Different versions of readline do different things with the smm code. stdout = stdout.replace(SMM, b'').strip() # Note: this test will fail in versions of IPython < 4.1.0 because of a # bug. See https://github.com/ipython/ipython/issues/8724 and # https://github.com/ipython/ipython/pull/8738. assert stdout == b"""\ \x01\x1b]133;D;0\x07\x02\x01\x1b]133;A\x07\x02In [1]: \x01\x1b]133;B\x07\x02\x1b]133;C\x07Out[1]: 1 \x01\x1b]133;D;0\x07\x02\x01\x1b]133;A\x07\x02In [2]: \x01\x1b]133;B\x07\x02 \x01\x1b]133;D;0\x07\x02\x01\x1b]133;A\x07\x02In [2]: \x01\x1b]133;B\x07\x02\x1b]133;C\x07--------------------------------------------------------------------------- Exception Traceback (most recent call last) <ipython-input-2-fca2ab0ca76b> in <module>() ----> 1 raise Exception Exception: \n\n\x01\x1b]133;D;1\x07\x02\x01\x1b]133;A\x07\x02In [3]: \x01\x1b]133;B\x07\x02\x1b]133;C\x07--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-3-002bcaa7be0e> in <module>() ----> 1 undefined NameError: name 'undefined' is not defined \x01\x1b]133;D;1\x07\x02\x01\x1b]133;A\x07\x02In [4]: \x01\x1b]133;B\x07\x02 ...: ...: \x1b]133;C\x07 \x01\x1b]133;D;0\x07\x02\x01\x1b]133;A\x07\x02In [5]: \x01\x1b]133;B\x07\x02\x1b]133;C\x07 \x01\x1b]133;D;0\x07\x02\x01\x1b]133;A\x07\x02In [6]: \x01\x1b]133;B\x07\x02 \x01\x1b]133;D;0\x07\x02\x01\x1b]133;A\x07\x02In [6]: \x01\x1b]133;B\x07\x02 Do you really want to exit ([y]/n)?\ """ assert stderr == b'' # Ideally all the codes would be bytes in Python 3, but bytes don't have a # format (even in Python 3.5). stdout = stdout.decode('ascii') AFTER_OUTPUT0 = AFTER_OUTPUT.format(command_status=0) AFTER_OUTPUT1 = AFTER_OUTPUT.format(command_status=1) assert (stdout.count(AFTER_OUTPUT0) == stdout.count(readline_invisible(AFTER_OUTPUT0)) == stdout.count(readline_invisible(AFTER_OUTPUT0) + readline_invisible(BEFORE_PROMPT)) == 6) assert (stdout.count(AFTER_OUTPUT1) == stdout.count(readline_invisible(AFTER_OUTPUT1)) == stdout.count(readline_invisible(AFTER_OUTPUT1) + readline_invisible(BEFORE_PROMPT)) == 2) assert (stdout.count(BEFORE_PROMPT) == stdout.count(readline_invisible(BEFORE_PROMPT)) == 8) assert (stdout.count(AFTER_PROMPT) == stdout.count(readline_invisible(AFTER_PROMPT)) == 8) assert stdout.count(BEFORE_OUTPUT) == 5 assert stdout.count(readline_invisible(BEFORE_OUTPUT)) == 0 AFTER_OUTPUT_RE = re.compile(re.escape(AFTER_OUTPUT.format(command_status='DUMMY')).replace("DUMMY", r'\d')) assert re.findall(AFTER_OUTPUT_RE, stdout) == [ AFTER_OUTPUT0, AFTER_OUTPUT0, AFTER_OUTPUT0, AFTER_OUTPUT1, AFTER_OUTPUT1, AFTER_OUTPUT0, AFTER_OUTPUT0, AFTER_OUTPUT0, ] AFTER_PROMPT_RE = re.escape(AFTER_PROMPT) BEFORE_OUTPUT_RE = re.escape(BEFORE_OUTPUT) AFTER_PROMPT_OR_BEFORE_OUTPUT_RE = re.compile('(%s|%s)' % (AFTER_PROMPT_RE, BEFORE_OUTPUT_RE)) assert re.findall(AFTER_PROMPT_OR_BEFORE_OUTPUT_RE, stdout) == [ AFTER_PROMPT, BEFORE_OUTPUT, # non-empty prompt AFTER_PROMPT, # empty prompt AFTER_PROMPT, BEFORE_OUTPUT, AFTER_PROMPT, BEFORE_OUTPUT, AFTER_PROMPT, BEFORE_OUTPUT, AFTER_PROMPT, BEFORE_OUTPUT, AFTER_PROMPT, AFTER_PROMPT, ]
def test_readline_invisible(): assert readline_invisible(BEFORE_PROMPT) == "\001" + BEFORE_PROMPT + "\002"