Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions bash_kernel/exit_code_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import re

exit_code_format = "BASHKERNEL_RETCODE<<<{}>>>"
exit_code_re = re.compile(exit_code_format.format("([0-9]+)"))
exit_code_printf_format = exit_code_format.format("%d")
exit_code_command_checker = f'{{ printf "{exit_code_printf_format}" $?; }} 2>/dev/null'


def get_last_exit_code(bashwrapper):
output = bashwrapper.run_command(exit_code_command_checker)
matches = exit_code_re.findall(output)
return int(matches[0])
3 changes: 2 additions & 1 deletion bash_kernel/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
version_pat = re.compile(r'version (\d+(\.\d+)+)')

from .display import (extract_contents, build_cmds)
from .exit_code_checker import get_last_exit_code

class IREPLWrapper(replwrap.REPLWrapper):
"""A subclass of REPLWrapper that gives incremental output
Expand Down Expand Up @@ -224,7 +225,7 @@ def do_execute(self, code, silent, store_history=True,
return {'status': 'abort', 'execution_count': self.execution_count}

try:
exitcode = int(self.bashwrapper.run_command('{ echo $?; } 2>/dev/null').rstrip().split("\r\n")[0])
exitcode = get_last_exit_code(self.bashwrapper)
except Exception as exc:
exitcode = 1

Expand Down
63 changes: 63 additions & 0 deletions tests/test_exit_code_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import os
import pexpect
from bash_kernel.kernel import IREPLWrapper
from bash_kernel.exit_code_checker import get_last_exit_code
import pytest


def test_get_last_exit_code_command_not_found(bashwrapper):
bashwrapper.run_command("this-command-does-not-exist")
assert get_last_exit_code(bashwrapper) == 127


def test_get_last_exit_code_success(bashwrapper):
bashwrapper.run_command("echo")
assert get_last_exit_code(bashwrapper) == 0


def test_get_last_exit_code_command_not_found_conda(mock_conda_bashwrapper):
mock_conda_bashwrapper.run_command("this-command-does-not-exist")
assert get_last_exit_code(mock_conda_bashwrapper) == 127


def test_get_last_exit_code_success_conda(mock_conda_bashwrapper):
mock_conda_bashwrapper.run_command("echo")
assert get_last_exit_code(mock_conda_bashwrapper) == 0


@pytest.fixture(scope="session")
def mock_conda_bashwrapper(bashwrapper):
class FakeCondaWrapper:
def __init__(self):
self.child = bashwrapper

def run_command(self, cmd):
out = self.child.run_command(cmd)
# Simulating the effect of a conda env
# on the output of a wrapper
return out + "(condaenv)"

return FakeCondaWrapper()


# From BashKernel._start_bash
@pytest.fixture(scope="session")
def bashwrapper():
unique_prompt = "PROMPT_ASDFASFAS"
bashrc = os.path.join(os.path.dirname(pexpect.__file__), "bashrc.sh")
child = pexpect.spawn(
"bash",
["--rcfile", bashrc],
echo=False,
encoding="utf-8",
codec_errors="replace",
)

ps1 = unique_prompt + "\[\]" + ">"
ps2 = unique_prompt + "\[\]" + "+"
prompt_change = "PS1='{0}' PS2='{1}' PROMPT_COMMAND=''".format(ps1, ps2)
# Using IREPLWrapper to get incremental output
bashwrapper = IREPLWrapper(
child, "\$", prompt_change, unique_prompt, extra_init_cmd="export PAGER=cat"
)
return bashwrapper