xonsh script.xsh should not fail over

This commit is contained in:
Hugo Wang 2017-01-11 21:59:42 +08:00
parent c3ece749c5
commit 335fc71bc7
5 changed files with 38 additions and 12 deletions

14
news/failover-script.rst Normal file
View file

@ -0,0 +1,14 @@
**Added:** None
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:**
* Fixed an issue that xonsh would fail over to external shells when
running .xsh script which raises exceptions.
**Security:** None

1
tests/scripts/raise.xsh Normal file
View file

@ -0,0 +1 @@
raise Exception('oh no')

View file

@ -9,6 +9,7 @@ import sys
import xonsh.main import xonsh.main
import pytest import pytest
from tools import TEST_DIR
def Shell(*args, **kwargs): def Shell(*args, **kwargs):
@ -91,7 +92,7 @@ def test_xonsh_failback(shell, monkeypatch):
assert failback_checker == ['/bin/xshell', '/bin/xshell'] assert failback_checker == ['/bin/xshell', '/bin/xshell']
def test_xonsh_failback_non_interactive(shell, monkeypatch): def test_xonsh_failback_single(shell, monkeypatch):
class FakeFailureError(Exception): class FakeFailureError(Exception):
pass pass
@ -103,3 +104,17 @@ def test_xonsh_failback_non_interactive(shell, monkeypatch):
with pytest.raises(FakeFailureError): with pytest.raises(FakeFailureError):
xonsh.main.main() xonsh.main.main()
def test_xonsh_failback_script_from_file(shell, monkeypatch):
checker = []
def mocked_execlp(f, *args):
checker.append(f)
monkeypatch.setattr(os, 'execlp', mocked_execlp)
script = os.path.join(TEST_DIR, 'scripts', 'raise.xsh')
monkeypatch.setattr(sys, 'argv', ['xonsh', script])
monkeypatch.setattr(sys, 'stderr', open(os.devnull, 'w'))
with pytest.raises(Exception):
xonsh.main.main()
assert len(checker) == 0

View file

@ -24,6 +24,7 @@ ON_DARWIN = (platform.system() == 'Darwin')
ON_WINDOWS = (platform.system() == 'Windows') ON_WINDOWS = (platform.system() == 'Windows')
ON_CONDA = True in [conda in pytest.__file__ for conda ON_CONDA = True in [conda in pytest.__file__ for conda
in ['anaconda', 'miniconda']] in ['anaconda', 'miniconda']]
TEST_DIR = os.path.dirname(__file__)
# pytest skip decorators # pytest skip decorators
skip_if_py34 = pytest.mark.skipif(VER_MAJOR_MINOR < VER_3_5, reason="Py3.5+ only test") skip_if_py34 = pytest.mark.skipif(VER_MAJOR_MINOR < VER_3_5, reason="Py3.5+ only test")

View file

@ -253,17 +253,11 @@ def premain(argv=None):
return args return args
def _failback_to_other_shells(argv, err): def _failback_to_other_shells(args, err):
args = None
try:
args = premain(argv)
except Exception:
pass
# only failback for interactive shell; if we cannot tell, treat it # only failback for interactive shell; if we cannot tell, treat it
# as an interactive one for safe. # as an interactive one for safe.
if hasattr(args, 'mode') and args.mode != XonshMode.interactive: if hasattr(args, 'mode') and args.mode != XonshMode.interactive:
raise err raise err
foreign_shell = None foreign_shell = None
shells_file = '/etc/shells' shells_file = '/etc/shells'
if not os.path.exists(shells_file): if not os.path.exists(shells_file):
@ -294,15 +288,16 @@ def _failback_to_other_shells(argv, err):
def main(argv=None): def main(argv=None):
args = None
try: try:
return main_xonsh(argv) args = premain(argv)
return main_xonsh(args)
except Exception as err: except Exception as err:
_failback_to_other_shells(argv, err) _failback_to_other_shells(args, err)
def main_xonsh(argv=None): def main_xonsh(args):
"""Main entry point for xonsh cli.""" """Main entry point for xonsh cli."""
args = premain(argv)
events.on_post_init.fire() events.on_post_init.fire()
env = builtins.__xonsh_env__ env = builtins.__xonsh_env__
shell = builtins.__xonsh_shell__ shell = builtins.__xonsh_shell__