# -*- coding: utf-8 -*- """Tests the xonsh main function.""" from __future__ import unicode_literals, print_function from contextlib import contextmanager import builtins import gc import os import os.path import sys import xonsh.main from xonsh.main import XonshMode import pytest from tools import ON_WINDOWS, TEST_DIR, VER_FULL, skip_if_on_windows def Shell(*args, **kwargs): pass @pytest.fixture def shell(xonsh_builtins, monkeypatch): """Xonsh Shell Mock""" if hasattr(builtins, "__xonsh__"): builtins.__xonsh__.unlink_builtins() del builtins.__xonsh__ for xarg in dir(builtins): if "__xonsh_" in xarg: delattr(builtins, xarg) gc.collect() Shell.shell_type_aliases = {"rl": "readline"} monkeypatch.setattr(xonsh.main, "Shell", Shell) def test_premain_no_arg(shell, monkeypatch): monkeypatch.setattr(sys.stdin, "isatty", lambda: True) xonsh.main.premain([]) assert builtins.__xonsh__.env.get("XONSH_LOGIN") def test_premain_interactive(shell): xonsh.main.premain(["-i"]) assert builtins.__xonsh__.env.get("XONSH_INTERACTIVE") def test_premain_login_command(shell): xonsh.main.premain(["-l", "-c", 'echo "hi"']) assert builtins.__xonsh__.env.get("XONSH_LOGIN") def test_premain_login(shell): xonsh.main.premain(["-l"]) assert builtins.__xonsh__.env.get("XONSH_LOGIN") def test_premain_D(shell): xonsh.main.premain(["-DTEST1=1616", "-DTEST2=LOL"]) assert builtins.__xonsh__.env.get("TEST1") == "1616" assert builtins.__xonsh__.env.get("TEST2") == "LOL" def test_premain_custom_rc(shell, tmpdir, monkeypatch): monkeypatch.setattr(sys.stdin, "isatty", lambda: True) monkeypatch.setitem(os.environ, "XONSH_CACHE_SCRIPTS", "False") f = tmpdir.join("wakkawakka") f.write("print('hi')") args = xonsh.main.premain(["--rc", f.strpath]) assert args.mode == XonshMode.interactive assert f.strpath in builtins.__xonsh__.env.get("XONSHRC") def test_rc_with_modules(shell, tmpdir, monkeypatch, capsys): """Test that an RC file can load modules inside the same folder it is located in.""" monkeypatch.setattr(sys.stdin, "isatty", lambda: True) monkeypatch.setitem(os.environ, "XONSH_CACHE_SCRIPTS", "False") tmpdir.join("my_python_module.py").write("print('Hello,')") tmpdir.join("my_xonsh_module.xsh").write("print('World!')") rc = tmpdir.join("rc.xsh") rc.write("from my_python_module import *\nfrom my_xonsh_module import *") xonsh.main.premain(["--rc", rc.strpath]) assert rc.strpath in builtins.__xonsh__.env.get("XONSHRC") assert ( builtins.__xonsh__.env.get("LOADED_RC_FILES")[ builtins.__xonsh__.env.get("XONSHRC").index(rc.strpath) ] is True ) stdout, stderr = capsys.readouterr() assert "Hello,\nWorld!" in stdout assert len(stderr) == 0 # Check that the temporary rc's folder is not left behind on the path assert tmpdir.strpath not in sys.path @pytest.mark.skipif(ON_WINDOWS, reason="See https://github.com/xonsh/xonsh/issues/3936") def test_rc_with_modified_path(shell, tmpdir, monkeypatch, capsys): """Test that an RC file can edit the sys.path variable without losing those values.""" monkeypatch.setattr(sys.stdin, "isatty", lambda: True) monkeypatch.setitem(os.environ, "XONSH_CACHE_SCRIPTS", "False") rc = tmpdir.join("rc.xsh") rc.write(f"import sys\nsys.path.append('{tmpdir.strpath}')\nprint('Hello, World!')") xonsh.main.premain(["--rc", rc.strpath]) assert rc.strpath in builtins.__xonsh__.env.get("XONSHRC") assert ( builtins.__xonsh__.env.get("LOADED_RC_FILES")[ builtins.__xonsh__.env.get("XONSHRC").index(rc.strpath) ] is True ) stdout, stderr = capsys.readouterr() assert "Hello, World!" in stdout assert len(stderr) == 0 # Check that the path that was explicitly added is not accidentally deleted assert tmpdir.strpath in sys.path def test_rc_with_failing_module(shell, tmpdir, monkeypatch, capsys): """Test that an RC file which imports a module that throws an exception .""" monkeypatch.setattr(sys.stdin, "isatty", lambda: True) monkeypatch.setitem(os.environ, "XONSH_CACHE_SCRIPTS", "False") tmpdir.join("my_failing_module.py").write("raise RuntimeError('Unexpected error')") rc = tmpdir.join("rc.xsh") rc.write("from my_failing_module import *") xonsh.main.premain(["--rc", rc.strpath]) assert rc.strpath in builtins.__xonsh__.env.get("XONSHRC") assert ( builtins.__xonsh__.env.get("LOADED_RC_FILES")[ builtins.__xonsh__.env.get("XONSHRC").index(rc.strpath) ] is False ) stdout, stderr = capsys.readouterr() assert len(stdout) == 0 assert "RuntimeError: Unexpected error" in stderr # Check that the temporary rc's folder is not left behind on the path assert tmpdir.strpath not in sys.path def test_no_rc_with_script(shell, tmpdir): args = xonsh.main.premain(["tests/sample.xsh"]) assert not (args.mode == XonshMode.interactive) def test_force_interactive_rc_with_script(shell, tmpdir): xonsh.main.premain(["-i", "tests/sample.xsh"]) assert builtins.__xonsh__.env.get("XONSH_INTERACTIVE") def test_force_interactive_custom_rc_with_script(shell, tmpdir, monkeypatch): """Calling a custom RC file on a script-call with the interactive flag should run interactively """ monkeypatch.setitem(os.environ, "XONSH_CACHE_SCRIPTS", "False") f = tmpdir.join("wakkawakka") f.write("print('hi')") args = xonsh.main.premain(["-i", "--rc", f.strpath, "tests/sample.xsh"]) assert args.mode == XonshMode.interactive assert f.strpath in builtins.__xonsh__.env.get("XONSHRC") def test_custom_rc_with_script(shell, tmpdir): """Calling a custom RC file on a script-call without the interactive flag should not run interactively """ f = tmpdir.join("wakkawakka") f.write("print('hi')") args = xonsh.main.premain(["--rc", f.strpath, "tests/sample.xsh"]) assert not (args.mode == XonshMode.interactive) def test_premain_no_rc(shell, tmpdir): xonsh.main.premain(["--no-rc", "-i"]) assert not builtins.__xonsh__.env.get("XONSHRC") @pytest.mark.parametrize( "arg", ["", "-i", "-vERSION", "-hAALP", "TTTT", "-TT", "--TTT"] ) def test_premain_with_file_argument(arg, shell): xonsh.main.premain(["tests/sample.xsh", arg]) assert not (builtins.__xonsh__.env.get("XONSH_INTERACTIVE")) def test_premain_interactive__with_file_argument(shell): xonsh.main.premain(["-i", "tests/sample.xsh"]) assert builtins.__xonsh__.env.get("XONSH_INTERACTIVE") @pytest.mark.parametrize("case", ["----", "--hep", "-TT", "--TTTT"]) def test_premain_invalid_arguments(shell, case, capsys): with pytest.raises(SystemExit): xonsh.main.premain([case]) assert "unrecognized argument" in capsys.readouterr()[1] def test_premain_timings_arg(shell): xonsh.main.premain(["--timings"]) @skip_if_on_windows @pytest.mark.parametrize( ("env_shell", "rc_shells", "exp_shell"), [ ("", [], ""), ("/argle/bash", [], "/argle/bash"), ("/bin/xonsh", [], ""), ( "/argle/bash", ["/argle/xonsh", "/argle/dash", "/argle/sh", "/argle/bargle"], "/argle/bash", ), ( "", ["/argle/xonsh", "/argle/dash", "/argle/sh", "/argle/bargle"], "/argle/dash", ), ("", ["/argle/xonsh", "/argle/screen", "/argle/sh"], "/argle/sh"), ("", ["/argle/xonsh", "/argle/screen"], ""), ], ) @skip_if_on_windows def test_xonsh_failback( env_shell, rc_shells, exp_shell, shell, xonsh_builtins, monkeypatch, monkeypatch_stderr, ): failback_checker = [] def mocked_main(*args): raise Exception("A fake failure") monkeypatch.setattr(xonsh.main, "main_xonsh", mocked_main) def mocked_execlp(f, *args): failback_checker.append(f) failback_checker.append(args[0]) monkeypatch.setattr(os, "execlp", mocked_execlp) monkeypatch.setattr(os.path, "exists", lambda x: True) monkeypatch.setattr(sys, "argv", ["/bin/xonsh", "-i"]) # has to look like real path @contextmanager def mocked_open(*args): yield rc_shells monkeypatch.setattr(builtins, "open", mocked_open) monkeypatch.setenv("SHELL", env_shell) try: xonsh.main.main() # if main doesn't raise, it did try to invoke a shell assert failback_checker[0] == exp_shell assert failback_checker[1] == failback_checker[0] except Exception as e: if len(e.args) and "A fake failure" in str( e.args[0] ): # if it did raise expected exception assert len(failback_checker) == 0 # then it didn't invoke a shell else: raise e # it raised something other than the test exception, def test_xonsh_failback_single(shell, monkeypatch, monkeypatch_stderr): class FakeFailureError(Exception): pass def mocked_main(*args): raise FakeFailureError() monkeypatch.setattr(xonsh.main, "main_xonsh", mocked_main) monkeypatch.setattr(sys, "argv", ["xonsh", "-c", "echo", "foo"]) with pytest.raises(FakeFailureError): xonsh.main.main() def test_xonsh_failback_script_from_file(shell, monkeypatch, monkeypatch_stderr): 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]) with pytest.raises(Exception): xonsh.main.main() assert len(checker) == 0 def test_xonsh_no_file_returncode(shell, monkeypatch, monkeypatch_stderr): monkeypatch.setattr(sys, "argv", ["xonsh", "foobazbarzzznotafileatall.xsh"]) with pytest.raises(SystemExit): xonsh.main.main()