mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Loading rc in non-interactive login shell (#3422)
* Setting -l flag loads environment on non-interactive shell * Added checking dash existance in sys.argv[0] * Fixed -a switch behaviour * Added tests for xexec * Added test for python<3.7 on Windows * Testing non existent command with mocked execvpe
This commit is contained in:
parent
1b1c668049
commit
b37675a6e8
5 changed files with 156 additions and 13 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -36,6 +36,7 @@ pip-selfcheck.json
|
||||||
bin/
|
bin/
|
||||||
/lib/
|
/lib/
|
||||||
include/
|
include/
|
||||||
|
venv/
|
||||||
|
|
||||||
# Mac
|
# Mac
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
24
news/extended-exec.rst
Normal file
24
news/extended-exec.rst
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* Added ``-l``, ``-c`` and ``-a`` options to ``xexec``, works now like ``exec``
|
||||||
|
in bash/zsh
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* ``-l`` switch works like bash, loads environment in non-interactive shell
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
84
tests/aliases/test_xexec.py
Normal file
84
tests/aliases/test_xexec.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import os
|
||||||
|
import inspect
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from xonsh.aliases import xexec
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mockexecvpe(monkeypatch):
|
||||||
|
def mocked_execvpe(_command, _args, _env):
|
||||||
|
pass
|
||||||
|
|
||||||
|
monkeypatch.setattr(os, "execvpe", mocked_execvpe)
|
||||||
|
|
||||||
|
|
||||||
|
def test_noargs(mockexecvpe):
|
||||||
|
assert xexec([]) == (None, "xonsh: exec: no args specified\n", 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_command(mockexecvpe):
|
||||||
|
assert xexec(["-a", "foo"]) == (None, "xonsh: exec: no command specified\n", 1)
|
||||||
|
assert xexec(["-c"]) == (None, "xonsh: exec: no command specified\n", 1)
|
||||||
|
assert xexec(["-l"]) == (None, "xonsh: exec: no command specified\n", 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_command_not_found(monkeypatch):
|
||||||
|
|
||||||
|
dummy_error_msg = "This is dummy error message, file not found or something like that"
|
||||||
|
command = "non_existing_command"
|
||||||
|
|
||||||
|
def mocked_execvpe(_command, _args, _env):
|
||||||
|
raise FileNotFoundError(2, dummy_error_msg)
|
||||||
|
monkeypatch.setattr(os, "execvpe", mocked_execvpe)
|
||||||
|
|
||||||
|
assert xexec([command]) == (None,
|
||||||
|
"xonsh: exec: file not found: {}: {}" "\n".format(dummy_error_msg, command),
|
||||||
|
1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_help(mockexecvpe):
|
||||||
|
assert xexec(["-h"]) == inspect.getdoc(xexec)
|
||||||
|
assert xexec(["--help"]) == inspect.getdoc(xexec)
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_switch(monkeypatch):
|
||||||
|
called = {}
|
||||||
|
|
||||||
|
def mocked_execvpe(command, args, env):
|
||||||
|
called.update({"command": command, "args": args, "env": env})
|
||||||
|
|
||||||
|
monkeypatch.setattr(os, "execvpe", mocked_execvpe)
|
||||||
|
proc_name = "foo"
|
||||||
|
command = "bar"
|
||||||
|
command_args = ["1"]
|
||||||
|
xexec(["-a", proc_name, command] + command_args)
|
||||||
|
assert called["command"] == command
|
||||||
|
assert called["args"][0] == proc_name
|
||||||
|
assert len(called["args"]) == len([command] + command_args)
|
||||||
|
|
||||||
|
|
||||||
|
def test_l_switch(monkeypatch):
|
||||||
|
called = {}
|
||||||
|
|
||||||
|
def mocked_execvpe(command, args, env):
|
||||||
|
called.update({"command": command, "args": args, "env": env})
|
||||||
|
|
||||||
|
monkeypatch.setattr(os, "execvpe", mocked_execvpe)
|
||||||
|
command = "bar"
|
||||||
|
xexec(["-l", command, "1"])
|
||||||
|
|
||||||
|
assert called["args"][0].startswith("-")
|
||||||
|
|
||||||
|
|
||||||
|
def test_c_switch(monkeypatch):
|
||||||
|
called = {}
|
||||||
|
|
||||||
|
def mocked_execvpe(command, args, env):
|
||||||
|
called.update({"command": command, "args": args, "env": env})
|
||||||
|
|
||||||
|
monkeypatch.setattr(os, "execvpe", mocked_execvpe)
|
||||||
|
command = "sleep"
|
||||||
|
xexec(["-c", command, "1"])
|
||||||
|
assert called["env"] == {}
|
|
@ -610,7 +610,7 @@ def source_cmd(args, stdin=None):
|
||||||
|
|
||||||
|
|
||||||
def xexec(args, stdin=None):
|
def xexec(args, stdin=None):
|
||||||
"""exec [-h|--help] command [args...]
|
"""exec [-h|--help] [-cl] [-a name] command [args...]
|
||||||
|
|
||||||
exec (also aliased as xexec) uses the os.execvpe() function to
|
exec (also aliased as xexec) uses the os.execvpe() function to
|
||||||
replace the xonsh process with the specified program. This provides
|
replace the xonsh process with the specified program. This provides
|
||||||
|
@ -620,6 +620,13 @@ def xexec(args, stdin=None):
|
||||||
bash $
|
bash $
|
||||||
|
|
||||||
The '-h' and '--help' options print this message and exit.
|
The '-h' and '--help' options print this message and exit.
|
||||||
|
If the '-l' option is supplied, the shell places a dash at the
|
||||||
|
beginning of the zeroth argument passed to command to simulate login
|
||||||
|
shell.
|
||||||
|
The '-c' option causes command to be executed with an empty environment.
|
||||||
|
If '-a' is supplied, the shell passes name as the zeroth argument
|
||||||
|
to the executed command.
|
||||||
|
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
|
@ -633,18 +640,40 @@ def xexec(args, stdin=None):
|
||||||
"""
|
"""
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
return (None, "xonsh: exec: no args specified\n", 1)
|
return (None, "xonsh: exec: no args specified\n", 1)
|
||||||
elif args[0] == "-h" or args[0] == "--help":
|
|
||||||
|
parser = argparse.ArgumentParser(add_help=False)
|
||||||
|
parser.add_argument("-h", "--help", action="store_true")
|
||||||
|
parser.add_argument("-l", dest="login", action="store_true")
|
||||||
|
parser.add_argument("-c", dest="clean", action="store_true")
|
||||||
|
parser.add_argument("-a", dest="name", nargs="?")
|
||||||
|
parser.add_argument("command", nargs=argparse.REMAINDER)
|
||||||
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
|
if args.help:
|
||||||
return inspect.getdoc(xexec)
|
return inspect.getdoc(xexec)
|
||||||
else:
|
|
||||||
|
if len(args.command) == 0:
|
||||||
|
return (None, "xonsh: exec: no command specified\n", 1)
|
||||||
|
|
||||||
|
command = args.command[0]
|
||||||
|
if args.name is not None:
|
||||||
|
args.command[0] = args.name
|
||||||
|
if args.login:
|
||||||
|
args.command[0] = "-{}".format(args.command[0])
|
||||||
|
|
||||||
|
denv = {}
|
||||||
|
if not args.clean:
|
||||||
denv = builtins.__xonsh__.env.detype()
|
denv = builtins.__xonsh__.env.detype()
|
||||||
try:
|
|
||||||
os.execvpe(args[0], args, denv)
|
try:
|
||||||
except FileNotFoundError as e:
|
os.execvpe(command, args.command, denv)
|
||||||
return (
|
except FileNotFoundError as e:
|
||||||
None,
|
return (
|
||||||
"xonsh: exec: file not found: {}: {}" "\n".format(e.args[1], args[0]),
|
None,
|
||||||
1,
|
"xonsh: exec: file not found: {}: {}"
|
||||||
)
|
"\n".format(e.args[1], args.command[0]),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AWitchAWitch(argparse.Action):
|
class AWitchAWitch(argparse.Action):
|
||||||
|
|
|
@ -292,7 +292,11 @@ def start_services(shell_kwargs, args):
|
||||||
env = builtins.__xonsh__.env
|
env = builtins.__xonsh__.env
|
||||||
rc = shell_kwargs.get("rc", None)
|
rc = shell_kwargs.get("rc", None)
|
||||||
rc = env.get("XONSHRC") if rc is None else rc
|
rc = env.get("XONSHRC") if rc is None else rc
|
||||||
if args.mode != XonshMode.interactive and not args.force_interactive:
|
if (
|
||||||
|
args.mode != XonshMode.interactive
|
||||||
|
and not args.force_interactive
|
||||||
|
and not args.login
|
||||||
|
):
|
||||||
# Don't load xonshrc if not interactive shell
|
# Don't load xonshrc if not interactive shell
|
||||||
rc = None
|
rc = None
|
||||||
events.on_pre_rc.fire()
|
events.on_pre_rc.fire()
|
||||||
|
@ -329,7 +333,8 @@ def premain(argv=None):
|
||||||
"cacheall": args.cacheall,
|
"cacheall": args.cacheall,
|
||||||
"ctx": builtins.__xonsh__.ctx,
|
"ctx": builtins.__xonsh__.ctx,
|
||||||
}
|
}
|
||||||
if args.login:
|
if args.login or sys.argv[0].startswith("-"):
|
||||||
|
args.login = True
|
||||||
shell_kwargs["login"] = True
|
shell_kwargs["login"] = True
|
||||||
if args.norc:
|
if args.norc:
|
||||||
shell_kwargs["rc"] = ()
|
shell_kwargs["rc"] = ()
|
||||||
|
|
Loading…
Add table
Reference in a new issue