update test xsh usage (#4581)

* todo

* test: remove usage of DummyEnv and setting .env attribute on xession fixture

one step closer to making too much of tweaking to xession during tests

* test: fix tests vox and gitstatus-prompt

* docs: update test-fixture usage

* fix: import flake8 error

* test: remove direct access to XSH in tests

* test: remove usage of XSH in test files

* todo

* test: use tmp-dir to create stubs

* refactor: use fixture factory to mock out XonshSession

* refactor: remove direct access of XSH from functions

* refactor: remove direct access of XSH from functions

* fix: qa checks

* refactor: rename variables to match their values

* test: update failing tests because it had no PATH set previously

* fix: remove builtins usage from pyghooks.py

* style:

* refactor: update tests to use fixtures

* fix: env varialbe is setup per function

some tests accidentally update the env variables and that is leaking
into next tests

* fix: failing vox tests

* test: set commands_cache per test

* test: fix failing tests

* fix: failing tests on linux

ptk-highlight

* fix: failing tests on Windows

cwd-prompt

* test: copy env as to not affect original object

* fix: lazily evaluate cmds-cache in pyghooks

* test: fix failing tests

* fix: qa errors import

* test: set commands-cache per test

while caching path results

* test: speedup test_thread_local_swap

* fix: failing tests on windows

* refactor: Execer doesn't control session

* refactor: XSH.unload will take care of reversing builtins attrs set

* test: use env.update over monkeypatch

* Revert "test: use env.update over monkeypatch"

This reverts commit 010a5022247a098f1741966b8af1bf758663480e.
This commit is contained in:
Noorhteen Raja NJ 2022-01-08 04:03:22 +05:30 committed by GitHub
parent b6c61e3343
commit 7c4e207abd
Failed to generate hash of commit
42 changed files with 1444 additions and 1402 deletions

View file

@ -5,7 +5,7 @@ repos:
name: black
# this gets run within development environment.
# Otherwise will raise command not found or use system level binary
entry: black --check xonsh/ xontrib/ tests/
entry: black xonsh/ xontrib/ tests/
language: system
stages: [ commit ]
types:

View file

@ -246,8 +246,11 @@ parts of xonsh for more test isolation. For a list of the various fixtures::
when writing tests it's best to use pytest features i.e. parametrization::
@pytest.mark.parametrize('env', [test_env1, test_env2])
def test_one(env, xonsh_builtins):
xonsh_builtins.__xonsh__.env = env
def test_one(env, xession):
# update the environment variables instead of setting the attribute
# which could result in leaks to other tests.
# each run will have the same set of default env variables set.
xession.env.update(env)
...
this will run the test two times each time with the respective `test_env`.

View file

@ -1,6 +1,5 @@
import pytest
from xonsh.built_ins import XSH
from xonsh.completers._aliases import add_one_completer
from xonsh.completers.tools import non_exclusive_completer
@ -29,7 +28,8 @@ NON_EXCLUSIVE = non_exclusive_completer(lambda: None)
),
),
)
def test_add_completer_start(monkeypatch, initial, exp):
monkeypatch.setattr(XSH, "completers", initial)
def test_add_completer_start(monkeypatch, initial, exp, xession):
xession.completers.clear()
xession.completers.update(initial)
add_one_completer("new", SIMPLE, "start")
assert list(XSH.completers.keys()) == exp
assert list(xession.completers.keys()) == exp

View file

@ -8,7 +8,7 @@ from xonsh.aliases import source_alias, make_default_aliases
@pytest.fixture
def mockopen(xonsh_builtins, monkeypatch):
def mockopen(xession, monkeypatch):
@contextmanager
def mocked_open(fpath, *args, **kwargs):
yield MagicMock(read=lambda: fpath)
@ -33,7 +33,9 @@ def test_source_current_dir(mockopen, monkeypatch, mocked_execx_checker):
assert mocked_execx_checker == ["foo", "bar"]
def test_source_path(mockopen, mocked_execx_checker):
def test_source_path(mockopen, mocked_execx_checker, patch_locate_binary, xession):
patch_locate_binary(xession.commands_cache)
source_alias(["foo", "bar"])
path_foo = os.path.join("tests", "bin", "foo")
path_bar = os.path.join("tests", "bin", "bar")

View file

@ -15,9 +15,8 @@ from xonsh.completers.commands import complete_command, complete_skipper
@pytest.fixture(autouse=True)
def xs_orig_commands_cache(xession, monkeypatch, xonsh_execer):
xession.unload()
xession.load(execer=xonsh_execer)
def xs_orig_commands_cache(xession):
pass
def test_complete_command(completion_context_parse):

View file

@ -1,6 +1,5 @@
import pytest
from xonsh.environ import Env
from xonsh.parsers.completion_context import CompletionContextParser
from xonsh.completers.environment import complete_environment_vars
@ -19,7 +18,7 @@ def parser():
),
)
def test_simple(cmd, xession, monkeypatch, parser):
monkeypatch.setattr(xession, "env", Env({"WOW": 1}))
xession.env.update({"WOW": 1})
context = parser.parse(cmd, len(cmd))
comps, lprefix = complete_environment_vars(context)
@ -28,7 +27,7 @@ def test_simple(cmd, xession, monkeypatch, parser):
def test_rich_completions(xession, monkeypatch, parser):
monkeypatch.setattr(xession, "env", Env({"WOW": 1}))
xession.env.update({"WOW": 1})
xession.env.register("WOW", type=int, doc="Nice Docs!")
context = parser.parse("$WO", 3)

View file

@ -45,13 +45,12 @@ def test_pip_list_re1(line):
["pip show", "", {"setuptools", "wheel", "pip"}],
],
)
def test_completions(
line, prefix, exp, check_completer, xession, monkeypatch, session_vars
):
def test_completions(line, prefix, exp, check_completer, xession, os_env, monkeypatch):
# use the actual PATH from os. Otherwise subproc will fail on windows. `unintialized python...`
monkeypatch.setattr(xession, "env", os_env)
if ON_WINDOWS:
line = line.replace("pip", "pip.exe")
# needs original env for subproc all on all platforms
monkeypatch.setattr(xession, "env", session_vars["env"])
comps = check_completer(line, prefix=prefix)
assert comps.intersection(exp)

View file

@ -1,4 +1,3 @@
import builtins
import os
import sys
import types
@ -13,11 +12,10 @@ from xonsh.completer import Completer
from xonsh.execer import Execer
from xonsh.jobs import tasks
from xonsh.events import events
from xonsh.platform import ON_WINDOWS
from xonsh.parsers.completion_context import CompletionContextParser
from xonsh import commands_cache
from tools import DummyShell, sp, DummyEnv, DummyHistory
from tools import DummyShell, sp, DummyHistory, copy_env
@pytest.fixture
@ -28,29 +26,52 @@ def source_path():
@pytest.fixture
def xonsh_execer(monkeypatch):
def xonsh_execer(monkeypatch, xonsh_session):
"""Initiate the Execer with a mocked nop `load_builtins`"""
execer = Execer()
XSH.load(execer=execer)
# TODO this monkeypatch *shouldn't* be useful now.
monkeypatch.setattr(XSH, "execer", execer)
yield execer
yield xonsh_session.execer
@pytest.fixture
def xonsh_execer_exec(xonsh_execer):
def factory(input, **kwargs):
xonsh_execer.exec(input, **kwargs)
return True
return factory
@pytest.fixture
def xonsh_execer_parse(xonsh_execer):
def factory(input):
tree = XSH.execer.parse(input, ctx=None)
return tree
return factory
@pytest.fixture
def patch_commands_cache_bins(xession, tmp_path, monkeypatch):
def _factory(binaries: tp.List[str]):
if not xession.env.get("PATH"):
xession.env["PATH"] = [tmp_path]
xession.env["PATH"] = [tmp_path]
exec_mock = MagicMock(return_value=binaries)
monkeypatch.setattr(commands_cache, "executables_in", exec_mock)
cc = commands_cache.CommandsCache()
xession.commands_cache = cc
return cc
return xession.commands_cache
return _factory
@pytest.fixture
def patch_locate_binary(monkeypatch):
def locate_binary(self, name):
return os.path.join(os.path.dirname(__file__), "bin", name)
def factory(cc: commands_cache.CommandsCache):
monkeypatch.setattr(cc, "locate_binary", types.MethodType(locate_binary, cc))
return cc
return factory
@pytest.fixture
def monkeypatch_stderr(monkeypatch):
"""Monkeypath sys.stderr with no ResourceWarning."""
@ -70,88 +91,140 @@ def xonsh_events():
@pytest.fixture(scope="session")
def session_vars():
"""keep costly vars per session"""
def session_os_env():
"""Env with values from os.environ like real session"""
from xonsh.environ import Env, default_env
from xonsh.commands_cache import CommandsCache
execer = Execer()
XSH.load(execer=execer)
return {
"execer": execer,
"env": Env(default_env()),
"commands_cache": CommandsCache(),
return Env(default_env())
@pytest.fixture(scope="session")
def session_env():
"""Env with some initial values that doesn't load from os.environ"""
from xonsh.environ import Env
initial_vars = {
"UPDATE_OS_ENVIRON": False,
"XONSH_DEBUG": 1,
"XONSH_COLOR_STYLE": "default",
"VC_BRANCH_TIMEOUT": 1,
"XONSH_ENCODING": "utf-8",
"XONSH_ENCODING_ERRORS": "strict",
"COMMANDS_CACHE_SAVE_INTERMEDIATE": False,
}
env = Env(initial_vars)
return env
@pytest.fixture(scope="session")
def session_execer():
return Execer()
@pytest.fixture
def xonsh_builtins(monkeypatch, xonsh_events, session_vars):
"""Mock out most of the builtins xonsh attributes."""
old_builtins = dict(vars(builtins).items()) # type: ignore
def os_env(session_os_env):
"""A mutable copy of Original session_os_env"""
XSH.load(ctx={}, **session_vars)
return copy_env(session_os_env)
def locate_binary(self, name):
return os.path.join(os.path.dirname(__file__), "bin", name)
for attr, val in [
("env", DummyEnv()),
("shell", DummyShell()),
("help", lambda x: x),
("aliases", Aliases()),
("exit", False),
("history", DummyHistory()),
# ("subproc_captured", sp),
("subproc_uncaptured", sp),
("subproc_captured_stdout", sp),
("subproc_captured_inject", sp),
("subproc_captured_object", sp),
("subproc_captured_hiddenobject", sp),
]:
monkeypatch.setattr(XSH, attr, val)
@pytest.fixture
def env(tmpdir, session_env):
"""a mutable copy of session_env"""
env_copy = copy_env(session_env)
initial_vars = {"XONSH_DATA_DIR": str(tmpdir)}
if ON_WINDOWS:
XSH.env["PATHEXT"] = [".EXE", ".BAT", ".CMD"]
env_copy.update(initial_vars)
return env_copy
cc = XSH.commands_cache
monkeypatch.setattr(cc, "locate_binary", types.MethodType(locate_binary, cc))
monkeypatch.setattr(cc, "_cmds_cache", {})
for attr, val in [
("evalx", eval),
("execx", None),
("compilex", None),
# Unlike all the other stuff, this has to refer to the "real" one because all modules that would
# be firing events on the global instance.
("events", xonsh_events),
]:
# attributes to builtins are dynamicProxy and should pickup the following
monkeypatch.setattr(XSH.builtins, attr, val)
@pytest.fixture
def xonsh_session(xonsh_events, session_execer, os_env, monkeypatch) -> XonshSession:
"""a fixture to use where XonshSession is fully loaded without any mocks"""
# todo: remove using builtins for tests at all
yield builtins
XSH.load(
ctx={},
execer=session_execer,
commands_cache=commands_cache.CommandsCache(),
env=os_env,
)
yield XSH
XSH.unload()
for attr in set(dir(builtins)) - set(old_builtins):
if hasattr(builtins, attr):
delattr(builtins, attr)
for attr, old_value in old_builtins.items():
setattr(builtins, attr, old_value)
tasks.clear() # must to this to enable resetting all_jobs
@pytest.fixture
def xession(xonsh_builtins) -> XonshSession:
return XSH
def mock_xonsh_session(monkeypatch, xonsh_events, xonsh_session, env):
"""Mock out most of the builtins xonsh attributes."""
# make sure that all other fixtures call this mock only one time
session = []
def factory(*attrs: str):
"""
Parameters
----------
attrs
do not mock the given attributes
Returns
-------
XonshSession
with most of the attributes mocked out
"""
if session:
raise RuntimeError("The factory should be called only once per test")
for attr, val in [
("env", env),
("shell", DummyShell()),
("help", lambda x: x),
("aliases", Aliases()),
("exit", False),
("history", DummyHistory()),
(
"commands_cache",
commands_cache.CommandsCache(),
), # since env,aliases change , patch cmds-cache
# ("subproc_captured", sp),
("subproc_uncaptured", sp),
("subproc_captured_stdout", sp),
("subproc_captured_inject", sp),
("subproc_captured_object", sp),
("subproc_captured_hiddenobject", sp),
]:
if attr in attrs:
continue
monkeypatch.setattr(xonsh_session, attr, val)
for attr, val in [
("evalx", eval),
("execx", None),
("compilex", None),
# Unlike all the other stuff, this has to refer to the "real" one because all modules that would
# be firing events on the global instance.
("events", xonsh_events),
]:
# attributes to builtins are dynamicProxy and should pickup the following
monkeypatch.setattr(xonsh_session.builtins, attr, val)
session.append(xonsh_session)
return xonsh_session
yield factory
session.clear()
@pytest.fixture
def xsh_with_aliases(xession, monkeypatch):
from xonsh.aliases import Aliases, make_default_aliases
def xession(mock_xonsh_session) -> XonshSession:
"""Mock out most of the builtins xonsh attributes."""
return mock_xonsh_session()
xsh = xession
monkeypatch.setattr(xsh, "aliases", Aliases(make_default_aliases()))
return xsh
@pytest.fixture
def xsh_with_aliases(mock_xonsh_session) -> XonshSession:
return mock_xonsh_session("aliases")
@pytest.fixture(scope="session")
@ -160,7 +233,7 @@ def completion_context_parse():
@pytest.fixture
def check_completer(xession):
def check_completer():
"""Helper function to run completer and parse the results as set of strings"""
completer = Completer()

View file

@ -6,7 +6,6 @@ from subprocess import Popen
import pytest
from xonsh.procs.specs import cmds_to_specs, run_subproc
from xonsh.built_ins import XSH
from xonsh.procs.posix import PopenThread
from xonsh.procs.proxies import ProcProxy, ProcProxyThread, STDOUT_DISPATCHER
@ -49,8 +48,8 @@ def test_cmds_to_specs_thread_subproc(xession):
@pytest.mark.parametrize("thread_subprocs", [True, False])
def test_cmds_to_specs_capture_stdout_not_stderr(thread_subprocs):
env = XSH.env
def test_cmds_to_specs_capture_stdout_not_stderr(thread_subprocs, xonsh_session):
env = xonsh_session.env
cmds = (["ls", "/root"],)
env["THREAD_SUBPROCS"] = thread_subprocs
@ -68,7 +67,7 @@ def test_cmds_to_specs_capture_stdout_not_stderr(thread_subprocs):
)
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_capture_always(
capfd, thread_subprocs, capture_always, alias_type, pipe, monkeypatch
capfd, thread_subprocs, capture_always, alias_type, pipe, monkeypatch, xonsh_session
):
if not thread_subprocs and alias_type in ["func", "exec"]:
if pipe:
@ -76,7 +75,7 @@ def test_capture_always(
else:
return pytest.skip("https://github.com/xonsh/xonsh/issues/4444")
env = XSH.env
env = xonsh_session.env
exp = "HELLO\nBYE\n"
cmds = [["echo", "-n", exp]]
if pipe:
@ -88,15 +87,15 @@ def test_capture_always(
# Enable capfd for function aliases:
monkeypatch.setattr(STDOUT_DISPATCHER, "default", sys.stdout)
if alias_type == "func":
XSH.aliases["tst"] = (
xonsh_session.aliases["tst"] = (
lambda: run_subproc([first_cmd], "hiddenobject") and None
) # Don't return a value
elif alias_type == "exec":
first_cmd = " ".join(repr(arg) for arg in first_cmd)
XSH.aliases["tst"] = f"![{first_cmd}]"
xonsh_session.aliases["tst"] = f"![{first_cmd}]"
else:
# alias_type == "simple"
XSH.aliases["tst"] = first_cmd
xonsh_session.aliases["tst"] = first_cmd
cmds[0] = ["tst"]

View file

@ -2,7 +2,6 @@ from unittest.mock import Mock
import pytest
from xonsh.environ import Env
from xonsh.prompt.base import PromptFormatter, PROMPT_FIELDS
@ -76,8 +75,6 @@ def test_format_prompt_with_broken_template_in_func(inp, formatter):
def test_format_prompt_with_invalid_func(formatter, xession):
xession.env = Env()
def p():
foo = bar # raises exception # noqa
return "{user}"
@ -86,7 +83,6 @@ def test_format_prompt_with_invalid_func(formatter, xession):
def test_format_prompt_with_func_that_raises(formatter, capsys, xession):
xession.env = Env()
template = "tt {zerodiv} tt"
exp = "tt {BACKGROUND_RED}{ERROR:zerodiv}{RESET} tt"
fields = {"zerodiv": lambda: 1 / 0}
@ -96,13 +92,11 @@ def test_format_prompt_with_func_that_raises(formatter, capsys, xession):
assert "prompt: error" in err
def test_format_prompt_with_no_env(formatter, xession, live_fields):
def test_format_prompt_with_no_env(formatter, xession, live_fields, env):
xession.shell.prompt_formatter = formatter
env = Env()
env.pop("VIRTUAL_ENV", None) # For virtualenv
env.pop("CONDA_DEFAULT_ENV", None) # For conda/CircleCI
xession.env = env
assert formatter("{env_name}", fields=live_fields) == ""
@ -111,8 +105,7 @@ def test_format_prompt_with_no_env(formatter, xession, live_fields):
def test_format_prompt_with_various_envs(formatter, xession, live_fields, envname):
xession.shell.prompt_formatter = formatter
env = Env(VIRTUAL_ENV=envname)
xession.env = env
xession.env["VIRTUAL_ENV"] = envname
exp = live_fields["env_prefix"] + envname + live_fields["env_postfix"]
assert formatter("{env_name}", fields=live_fields) == exp
@ -123,8 +116,7 @@ def test_format_prompt_with_various_envs(formatter, xession, live_fields, envnam
def test_format_prompt_with_various_prepost(formatter, xession, live_fields, pre, post):
xession.shell.prompt_formatter = formatter
env = Env(VIRTUAL_ENV="env")
xession.env = env
xession.env["VIRTUAL_ENV"] = "env"
live_fields.update({"env_prefix": pre, "env_postfix": post})
@ -134,9 +126,7 @@ def test_format_prompt_with_various_prepost(formatter, xession, live_fields, pre
def test_noenv_with_disable_set(formatter, xession, live_fields):
xession.shell.prompt_formatter = formatter
env = Env(VIRTUAL_ENV="env", VIRTUAL_ENV_DISABLE_PROMPT=1)
xession.env = env
xession.env.update(dict(VIRTUAL_ENV="env", VIRTUAL_ENV_DISABLE_PROMPT=1))
exp = ""
assert formatter("{env_name}", fields=live_fields) == exp
@ -148,10 +138,13 @@ def test_custom_env_overrides_default(formatter, xession, live_fields, disable):
prompt = "!venv active! "
env = Env(
VIRTUAL_ENV="env", VIRTUAL_ENV_PROMPT=prompt, VIRTUAL_ENV_DISABLE_PROMPT=disable
xession.env.update(
dict(
VIRTUAL_ENV="env",
VIRTUAL_ENV_PROMPT=prompt,
VIRTUAL_ENV_DISABLE_PROMPT=disable,
)
)
xession.env = env
exp = "" if disable else prompt
assert formatter("{env_name}", fields=live_fields) == exp

View file

@ -1,16 +1,16 @@
from xonsh.prompt.cwd import _replace_home_cwd
from xonsh.built_ins import XSH
def test_cwd_escapes_curly_brackets_with_more_curly_brackets():
XSH.env["PWD"] = "{foo}"
def test_cwd_escapes_curly_brackets_with_more_curly_brackets(xession, tmpdir):
xession.env["HOME"] = str(tmpdir)
xession.env["PWD"] = "{foo}"
assert _replace_home_cwd() == "{{foo}}"
XSH.env["PWD"] = "{{foo}}"
xession.env["PWD"] = "{{foo}}"
assert _replace_home_cwd() == "{{{{foo}}}}"
XSH.env["PWD"] = "{"
xession.env["PWD"] = "{"
assert _replace_home_cwd() == "{{"
XSH.env["PWD"] = "}}"
xession.env["PWD"] = "}}"
assert _replace_home_cwd() == "}}}}"

View file

@ -3,6 +3,11 @@ import pytest
from xonsh.prompt import gitstatus
@pytest.fixture(autouse=True)
def git_no_stash(mocker):
return mocker.patch.object(gitstatus, "_get_stash", return_value=0)
@pytest.mark.parametrize(
"hidden, exp",
[

View file

@ -5,7 +5,6 @@ from unittest.mock import Mock
import pytest
from xonsh.environ import Env
from xonsh.prompt import vc
# Xonsh interaction with version control systems.
@ -46,7 +45,7 @@ defaultBranch = main
@pytest.fixture
def set_xenv(xession, monkeypatch):
def _wrapper(path):
monkeypatch.setattr(xession, "env", Env(VC_BRANCH_TIMEOUT=2, PWD=path))
xession.env.update(dict(VC_BRANCH_TIMEOUT=2, PWD=path))
return xession
return _wrapper

View file

@ -6,7 +6,6 @@ import pytest
import inspect
from xonsh.aliases import Aliases, ExecAlias
from xonsh.environ import Env
from tools import skip_if_on_windows
@ -26,7 +25,7 @@ def make_aliases():
return ales
def test_imports(xonsh_builtins):
def test_imports(xession):
ales = make_aliases()
expected = {
"o": ["omg", "lala"],
@ -39,17 +38,17 @@ def test_imports(xonsh_builtins):
assert raw == expected
def test_eval_normal(xonsh_builtins):
def test_eval_normal(xession):
ales = make_aliases()
assert ales.get("o") == ["omg", "lala"]
def test_eval_self_reference(xonsh_builtins):
def test_eval_self_reference(xession):
ales = make_aliases()
assert ales.get("ls") == ["ls", "- -"]
def test_eval_recursive(xonsh_builtins):
def test_eval_recursive(xession):
ales = make_aliases()
assert ales.get("color_ls") == ["ls", "- -", "--color=true"]
@ -57,7 +56,7 @@ def test_eval_recursive(xonsh_builtins):
@skip_if_on_windows
def test_eval_recursive_callable_partial(xonsh_execer, xession):
ales = make_aliases()
xession.env = Env(HOME=os.path.expanduser("~"))
xession.env["HOME"] = os.path.expanduser("~")
assert ales.get("indirect_cd")(["arg2", "arg3"]) == ["..", "arg2", "arg3"]
@ -74,7 +73,7 @@ def _return_to_sender_all(args, stdin, stdout, stderr, spec, stack):
)
def test_recursive_callable_partial_all(xonsh_builtins):
def test_recursive_callable_partial_all(xession):
ales = Aliases({"rtn": _return_to_sender_all, "rtn-recurse": ["rtn", "arg1"]})
alias = ales.get("rtn-recurse")
assert callable(alias)
@ -89,7 +88,7 @@ def _return_to_sender_handles(args, stdin, stdout, stderr):
return args, {"stdin": stdin, "stdout": stdout, "stderr": stderr}
def test_recursive_callable_partial_handles(xonsh_builtins):
def test_recursive_callable_partial_handles(xession):
ales = Aliases({"rtn": _return_to_sender_handles, "rtn-recurse": ["rtn", "arg1"]})
alias = ales.get("rtn-recurse")
assert callable(alias)
@ -104,7 +103,7 @@ def _return_to_sender_none():
return "wakka", {}
def test_recursive_callable_partial_none(xonsh_builtins):
def test_recursive_callable_partial_none(xession):
ales = Aliases({"rtn": _return_to_sender_none, "rtn-recurse": ["rtn"]})
alias = ales.get("rtn-recurse")
assert callable(alias)
@ -123,7 +122,7 @@ def test_recursive_callable_partial_none(xonsh_builtins):
"echo 'hi'; echo 'there'",
],
)
def test_subprocess_logical_operators(xonsh_builtins, alias):
def test_subprocess_logical_operators(xession, alias):
ales = make_aliases()
ales["echocat"] = alias
assert isinstance(ales["echocat"], ExecAlias)
@ -140,7 +139,7 @@ def test_subprocess_logical_operators(xonsh_builtins, alias):
"echo 'h|i << x > 3' | grep x",
],
)
def test_subprocess_io_operators(xonsh_builtins, alias):
def test_subprocess_io_operators(xession, alias):
ales = make_aliases()
ales["echocat"] = alias
assert isinstance(ales["echocat"], ExecAlias)
@ -152,7 +151,7 @@ def test_subprocess_io_operators(xonsh_builtins, alias):
{"echocat": "ls"},
],
)
def test_dict_merging(xonsh_builtins, alias):
def test_dict_merging(xession, alias):
ales = make_aliases()
assert (ales | alias)["echocat"] == ["ls"]
assert (alias | ales)["echocat"] == ["ls"]
@ -166,7 +165,7 @@ def test_dict_merging(xonsh_builtins, alias):
{"echocat": "echo Why?"},
],
)
def test_dict_merging_assignment(xonsh_builtins, alias):
def test_dict_merging_assignment(xession, alias):
ales = make_aliases()
ales |= alias
@ -180,7 +179,7 @@ def test_dict_merging_assignment(xonsh_builtins, alias):
assert alias["o"] == ales["o"]
def test_exec_alias_args(xonsh_builtins):
def test_exec_alias_args(xession):
stack = inspect.stack()
try:
ExecAlias("myargs = $args")(["arg0"], stack=stack)

View file

@ -6,7 +6,7 @@ from xonsh.ast import Tuple, Name, Store, min_line, Call, BinOp, isexpression
import pytest
from tools import check_parse, nodes_equal
from tools import nodes_equal
@pytest.fixture(autouse=True)
@ -45,20 +45,20 @@ def test_gather_load_store_names_tuple():
"l = 1", # ls remains undefined.
],
)
def test_multilline_num(xonsh_execer, line1):
def test_multilline_num(xonsh_execer_parse, line1):
# Subprocess transformation happens on the second line,
# because not all variables are known.
code = line1 + "\nls -l\n"
tree = check_parse(code)
tree = xonsh_execer_parse(code)
lsnode = tree.body[1]
assert 2 == min_line(lsnode)
assert isinstance(lsnode.value, Call)
def test_multilline_no_transform():
def test_multilline_no_transform(xonsh_execer_parse):
# No subprocess transformations happen here, since all variables are known.
code = "ls = 1\nl = 1\nls -l\n"
tree = check_parse(code)
tree = xonsh_execer_parse(code)
lsnode = tree.body[2]
assert 3 == min_line(lsnode)
assert isinstance(lsnode.value, BinOp)
@ -109,10 +109,10 @@ for root, dirs, files in os.walk(path):
""",
],
)
def test_unmodified(inp):
def test_unmodified(inp, xonsh_execer_parse):
# Context sensitive parsing should not modify AST
exp = pyast.parse(inp)
obs = check_parse(inp)
obs = xonsh_execer_parse(inp)
assert nodes_equal(exp, obs)
@ -121,8 +121,8 @@ def test_unmodified(inp):
"test_input",
["echo; echo && echo\n", "echo; echo && echo a\n", "true && false && true\n"],
)
def test_whitespace_subproc(test_input):
assert check_parse(test_input)
def test_whitespace_subproc(test_input, xonsh_execer_parse):
assert xonsh_execer_parse(test_input)
@pytest.mark.parametrize(

View file

@ -1,7 +1,6 @@
"""(A down payment on) Testing for ``xonsh.base_shell.BaseShell`` and associated classes"""
import os
from xonsh.environ import Env
from xonsh.base_shell import BaseShell
from xonsh.shell import transform_command
@ -9,8 +8,8 @@ from xonsh.shell import transform_command
def test_pwd_tracks_cwd(xession, xonsh_execer, tmpdir_factory, monkeypatch):
asubdir = str(tmpdir_factory.mktemp("asubdir"))
cur_wd = os.getcwd()
xession.env = Env(
PWD=cur_wd, XONSH_CACHE_SCRIPTS=False, XONSH_CACHE_EVERYTHING=False
xession.env.update(
dict(PWD=cur_wd, XONSH_CACHE_SCRIPTS=False, XONSH_CACHE_EVERYTHING=False)
)
monkeypatch.setattr(xonsh_execer, "cacheall", False, raising=False)

View file

@ -42,7 +42,7 @@ def test_reglob_tests(testfile):
@pytest.fixture
def home_env(xession):
"""Set `__xonsh__.env ` to a new Env instance on `xonsh_builtins`"""
xession.env = Env(HOME=HOME_PATH)
xession.env["HOME"] = HOME_PATH
return xession
@ -146,7 +146,7 @@ def test_list_of_strs_or_callables(exp, inp):
([["y", "z"], ["a", "b"]], ["ya", "yb", "za", "zb"]),
],
)
def test_list_of_list_of_strs_outer_product(xonsh_builtins, inp, exp):
def test_list_of_list_of_strs_outer_product(xession, inp, exp):
obs = list_of_list_of_strs_outer_product(inp)
assert exp == obs

View file

@ -15,27 +15,25 @@ from xonsh.commands_cache import (
from tools import skip_if_on_windows
def test_commands_cache_lazy(xonsh_builtins):
cc = CommandsCache()
def test_commands_cache_lazy(xession):
cc = xession.commands_cache
assert not cc.lazyin("xonsh")
assert 0 == len(list(cc.lazyiter()))
assert 0 == cc.lazylen()
def test_predict_threadable_unknown_command(xonsh_builtins):
cc = CommandsCache()
result = cc.predict_threadable(["command_should_not_found"])
def test_predict_threadable_unknown_command(xession):
result = xession.commands_cache.predict_threadable(["command_should_not_found"])
assert isinstance(result, bool)
@pytest.fixture
def commands_cache_tmp(xession, tmp_path, monkeypatch, patch_commands_cache_bins):
xession.env["XONSH_DATA_DIR"] = tmp_path
xession.env["COMMANDS_CACHE_SAVE_INTERMEDIATE"] = True
return patch_commands_cache_bins(["bin1", "bin2"])
def test_commands_cached_between_runs(commands_cache_tmp, tmp_path):
def test_commands_cached_between_runs(commands_cache_tmp, tmp_path, tmpdir):
# 1. no pickle file
# 2. return empty result first and create a thread to populate result
# 3. once the result is available then next call to cc.all_commands returns
@ -76,6 +74,7 @@ def test_commands_cache_uses_pickle_file(commands_cache_tmp, tmp_path, monkeypat
}
file.write_bytes(pickle.dumps(bins))
assert str(cc.cache_file) == str(file)
assert cc.all_commands == bins
assert cc._loaded_pickled
@ -112,14 +111,38 @@ def test_predict_shell_false(args):
PATTERN_BIN_USING_TTY_OR_NOT = [
(False, {10: b"isnotatty"}),
(False, {12: b"isatty"}),
(False, {151: b"gpm"}),
(False, {10: b"isatty", 100: b"tcgetattr"}),
(False, {10: b"isatty", 100: b"tcsetattr"}),
(True, {10: b"isatty", 100: b"tcsetattr", 1000: b"tcgetattr"}),
(True, {1000: b"libncurses"}),
(True, {4094: b"libgpm"}),
(
False,
{10: b"isnotatty"},
),
(
False,
{12: b"isatty"},
),
(
False,
{151: b"gpm"},
),
(
False,
{10: b"isatty", 100: b"tcgetattr"},
),
(
False,
{10: b"isatty", 100: b"tcsetattr"},
),
(
True,
{10: b"isatty", 100: b"tcsetattr", 1000: b"tcgetattr"},
),
(
True,
{1000: b"libncurses"},
),
(
True,
{4094: b"libgpm"},
),
(
True,
{2045: b"tcgetattr", 4095: b"tcgetattr", 6140: b"tcsetattr", 8190: b"isatty"},
@ -129,24 +152,23 @@ PATTERN_BIN_USING_TTY_OR_NOT = [
@pytest.mark.parametrize("args", PATTERN_BIN_USING_TTY_OR_NOT)
@skip_if_on_windows
def test_commands_cache_predictor_default(args, xonsh_builtins):
cc = CommandsCache()
def test_commands_cache_predictor_default(args, xession, tmp_path):
use_tty, patterns = args
f = open("testfile", "wb")
file = tmp_path / "testfile"
where = list(patterns.keys())
where.sort()
pos = 0
for w in where:
f.write(b"\x20" * (w - pos))
f.write(patterns[w])
pos = w + len(patterns[w])
with file.open("wb") as f:
pos = 0
for w in where:
f.write(b"\x20" * (w - pos))
f.write(patterns[w])
pos = w + len(patterns[w])
f.write(b"\x20" * (pos // 2))
f.close()
f.write(b"\x20" * (pos // 2))
result = cc.default_predictor_readbin(
"", os.getcwd() + os.sep + "testfile", timeout=1, failure=None
result = xession.commands_cache.default_predictor_readbin(
"", str(file), timeout=1, failure=None
)
expected = predict_false if use_tty else predict_true
assert result == expected
@ -154,26 +176,23 @@ def test_commands_cache_predictor_default(args, xonsh_builtins):
@skip_if_on_windows
def test_cd_is_only_functional_alias(xession):
cc = CommandsCache()
xession.aliases["cd"] = lambda args: os.chdir(args[0])
assert cc.is_only_functional_alias("cd")
xession.env["PATH"] = []
assert xession.commands_cache.is_only_functional_alias("cd")
def test_non_exist_is_only_functional_alias(xonsh_builtins):
cc = CommandsCache()
assert not cc.is_only_functional_alias("<not really a command name>")
def test_non_exist_is_only_functional_alias(xession):
assert not xession.commands_cache.is_only_functional_alias(
"<not really a command name>"
)
@skip_if_on_windows
def test_bash_is_only_functional_alias(xession):
xession.env["PATH"] = os.environ["PATH"].split(os.pathsep)
cc = CommandsCache()
assert not cc.is_only_functional_alias("bash")
assert not xession.commands_cache.is_only_functional_alias("bash")
@skip_if_on_windows
def test_bash_and_is_alias_is_only_functional_alias(xession):
xession.env["PATH"] = os.environ["PATH"].split(os.pathsep)
cc = CommandsCache()
xession.aliases["bash"] = lambda args: os.chdir(args[0])
assert not cc.is_only_functional_alias("bash")
assert not xession.commands_cache.is_only_functional_alias("bash")

View file

@ -2,7 +2,7 @@ import itertools
import typing as tp
import pytest
from unittest import mock
from xonsh.parsers.completion_context import (
CommandArg,
CommandContext,
@ -10,6 +10,9 @@ from xonsh.parsers.completion_context import (
PythonContext,
)
import xonsh.parsers.completion_context as ctx
from tests.tools import ON_WINDOWS
DEBUG = False
MISSING = object()
@ -21,8 +24,19 @@ PARSER: tp.Optional[CompletionContextParser] = None
def parser():
global PARSER
PARSER = CompletionContextParser(debug=DEBUG)
patcher = None
if ON_WINDOWS:
# on-windows has an option for interactive sessions. Overriding the lazyObject
patcher = mock.patch.object(
ctx,
"LINE_CONT_REPLACEMENT_DIFF",
("\\\n", "", -2),
)
patcher.start()
yield
PARSER = None
if ON_WINDOWS and patcher:
patcher.stop()
def parse(command, inner_index):

View file

@ -1,16 +1,8 @@
"""Tests xonsh contexts."""
from textwrap import dedent
from tools import check_exec
from xonsh.contexts import Block, Functor
import pytest
@pytest.fixture(autouse=True)
def xonsh_execer_autouse(xonsh_builtins, xonsh_execer):
return xonsh_execer
#
# helpers
@ -75,64 +67,64 @@ def block_checks_func(name, glbs, body, obsg=None, obsl=None):
#
def test_block_noexec():
def test_block_noexec(xonsh_execer_exec):
s = "x = 1\n" "with! Block():\n" " x += 42\n"
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
assert 1 == glbs["x"]
def test_block_oneline():
def test_block_oneline(xonsh_execer_exec):
body = " x += 42\n"
s = X1_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body, {"x": 1})
def test_block_manylines():
def test_block_manylines(xonsh_execer_exec):
body = " ![echo wow mom]\n" "# bad place for a comment\n" " x += 42"
s = X1_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body, {"x": 1})
def test_block_leading_comment():
def test_block_leading_comment(xonsh_execer_exec):
# leading comments do not show up in block lines
body = " # I am a leading comment\n" " x += 42\n"
s = X1_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, [" x += 42"], {"x": 1})
def test_block_trailing_comment():
def test_block_trailing_comment(xonsh_execer_exec):
# trailing comments show up in block lines
body = " x += 42\n" " # I am a trailing comment\n"
s = X1_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body, {"x": 1})
def test_block_trailing_line_continuation():
def test_block_trailing_line_continuation(xonsh_execer_exec):
body = " x += \\\n" " 42\n"
s = X1_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body, {"x": 1})
def test_block_trailing_close_paren():
def test_block_trailing_close_paren(xonsh_execer_exec):
body = ' x += int("42"\n' " )\n"
s = X1_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body, {"x": 1})
def test_block_trailing_close_many():
def test_block_trailing_close_many(xonsh_execer_exec):
body = (
' x = {None: [int("42"\n'
" )\n"
@ -141,69 +133,69 @@ def test_block_trailing_close_many():
)
s = SIMPLE_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body)
def test_block_trailing_triple_string():
def test_block_trailing_triple_string(xonsh_execer_exec):
body = ' x = """This\n' "is\n" '"probably"\n' "'not' what I meant.\n" '"""\n'
s = SIMPLE_WITH + body
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("b", glbs, body)
def test_block_func_oneline():
def test_block_func_oneline(xonsh_execer_exec):
body = " x += 42\n"
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
def test_block_func_manylines():
def test_block_func_manylines(xonsh_execer_exec):
body = " ![echo wow mom]\n" "# bad place for a comment\n" " x += 42\n"
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
def test_block_func_leading_comment():
def test_block_func_leading_comment(xonsh_execer_exec):
# leading comments do not show up in block lines
body = " # I am a leading comment\n" " x += 42\n"
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, " x += 42\n", FUNC_OBSG, FUNC_OBSL)
def test_block_func_trailing_comment():
def test_block_func_trailing_comment(xonsh_execer_exec):
# trailing comments show up in block lines
body = " x += 42\n" " # I am a trailing comment\n"
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
def test_blockfunc__trailing_line_continuation():
def test_blockfunc__trailing_line_continuation(xonsh_execer_exec):
body = " x += \\\n" " 42\n"
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
def test_block_func_trailing_close_paren():
def test_block_func_trailing_close_paren(xonsh_execer_exec):
body = ' x += int("42"\n' " )\n"
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
def test_block_func_trailing_close_many():
def test_block_func_trailing_close_many(xonsh_execer_exec):
body = (
' x = {None: [int("42"\n'
" )\n"
@ -212,15 +204,15 @@ def test_block_func_trailing_close_many():
)
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
def test_block_func_trailing_triple_string():
def test_block_func_trailing_triple_string(xonsh_execer_exec):
body = ' x = """This\n' "is\n" '"probably"\n' "'not' what I meant.\n" '"""\n'
s = FUNC_WITH.format(body=body)
glbs = {"Block": Block}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_func("rtn", glbs, body, FUNC_OBSG, FUNC_OBSL)
@ -231,55 +223,55 @@ def test_block_func_trailing_triple_string():
X2_WITH = "{var} = 1\n" "with! Functor() as f:\n" "{body}" "{var} += 1\n" "{calls}\n"
def test_functor_oneline_onecall_class():
def test_functor_oneline_onecall_class(xonsh_execer_exec):
body = " global y\n" " y += 42\n"
calls = "f()"
s = X2_WITH.format(body=body, calls=calls, var="y")
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"y": 44})
def test_functor_oneline_onecall_func():
def test_functor_oneline_onecall_func(xonsh_execer_exec):
body = " global z\n" " z += 42\n"
calls = "f.func()"
s = X2_WITH.format(body=body, calls=calls, var="z")
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"z": 44})
def test_functor_oneline_onecall_both():
def test_functor_oneline_onecall_both(xonsh_execer_exec):
body = " global x\n" " x += 42\n"
calls = "f()\nf.func()"
s = X2_WITH.format(body=body, calls=calls, var="x")
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"x": 86})
XA_WITH = "x = [1]\n" "with! Functor() as f:\n" "{body}" "x.append(2)\n" "{calls}\n"
def test_functor_oneline_append():
def test_functor_oneline_append(xonsh_execer_exec):
body = " x.append(3)\n"
calls = "f()\n"
s = XA_WITH.format(body=body, calls=calls)
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"x": [1, 2, 3]})
def test_functor_return():
def test_functor_return(xonsh_execer_exec):
body = " x = 42"
t = "res = 0\n" 'with! Functor(rtn="x") as f:\n' "{body}\n" "res = f()\n"
s = t.format(body=body)
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"res": 42})
def test_functor_args():
def test_functor_args(xonsh_execer_exec):
body = " x = 42 + a"
t = (
"res = 0\n"
@ -289,11 +281,11 @@ def test_functor_args():
)
s = t.format(body=body)
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"res": 44})
def test_functor_kwargs():
def test_functor_kwargs(xonsh_execer_exec):
body = " x = 42 + a + b"
t = (
"res = 0\n"
@ -303,11 +295,11 @@ def test_functor_kwargs():
)
s = t.format(body=body)
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"res": 49})
def test_functor_fullsig():
def test_functor_fullsig(xonsh_execer_exec):
body = " x = 42 + a + b + c"
t = (
"res = 0\n"
@ -317,5 +309,5 @@ def test_functor_fullsig():
)
s = t.format(body=body)
glbs = {"Functor": Functor}
check_exec(s, glbs=glbs, locs=None)
xonsh_execer_exec(s, glbs=glbs, locs=None)
block_checks_glb("f", glbs, body, {"res": 110})

View file

@ -6,7 +6,6 @@ import os
import pytest # noqa F401
from xonsh import dirstack
from xonsh.environ import Env
HERE = os.path.abspath(os.path.dirname(__file__))
@ -22,7 +21,7 @@ def chdir(adir):
def test_simple(xession):
xession.env = Env(CDPATH=PARENT, PWD=PARENT)
xession.env.update(dict(CDPATH=PARENT, PWD=PARENT))
with chdir(PARENT):
assert os.getcwd() != HERE
dirstack.cd(["tests"])
@ -30,7 +29,7 @@ def test_simple(xession):
def test_cdpath_simple(xession):
xession.env = Env(CDPATH=PARENT, PWD=HERE)
xession.env.update(dict(CDPATH=PARENT, PWD=HERE))
with chdir(os.path.normpath("/")):
assert os.getcwd() != HERE
dirstack.cd(["tests"])
@ -38,7 +37,7 @@ def test_cdpath_simple(xession):
def test_cdpath_collision(xession):
xession.env = Env(CDPATH=PARENT, PWD=HERE)
xession.env.update(dict(CDPATH=PARENT, PWD=HERE))
sub_tests = os.path.join(HERE, "tests")
if not os.path.exists(sub_tests):
os.mkdir(sub_tests)
@ -49,7 +48,7 @@ def test_cdpath_collision(xession):
def test_cdpath_expansion(xession):
xession.env = Env(HERE=HERE, CDPATH=("~", "$HERE"))
xession.env.update(dict(HERE=HERE, CDPATH=("~", "$HERE")))
test_dirs = (
os.path.join(HERE, "xonsh-test-cdpath-here"),
os.path.expanduser("~/xonsh-test-cdpath-home"),
@ -68,7 +67,7 @@ def test_cdpath_expansion(xession):
def test_cdpath_events(xession, tmpdir):
xession.env = Env(CDPATH=PARENT, PWD=os.getcwd())
xession.env.update(dict(CDPATH=PARENT, PWD=os.getcwd()))
target = str(tmpdir)
ev = None
@ -91,7 +90,7 @@ def test_cdpath_events(xession, tmpdir):
def test_cd_autopush(xession, tmpdir):
xession.env = Env(CDPATH=PARENT, PWD=os.getcwd(), AUTO_PUSHD=True)
xession.env.update(dict(CDPATH=PARENT, PWD=os.getcwd(), AUTO_PUSHD=True))
target = str(tmpdir)
old_dir = os.getcwd()

View file

@ -8,7 +8,6 @@ import sys
import pytest
from xonsh import dirstack
from xonsh.environ import Env
from xonsh.dirstack import DIRSTACK
from xonsh.platform import ON_WINDOWS
from xonsh.dirstack import _unc_tempDrives
@ -93,7 +92,7 @@ def shares_setup(tmpdir_factory):
def test_pushdpopd(xession):
"""Simple non-UNC push/pop to verify we didn't break nonUNC case."""
xession.env = Env(CDPATH=PARENT, PWD=HERE)
xession.env.update(dict(CDPATH=PARENT, PWD=HERE))
dirstack.cd([PARENT])
owd = os.getcwd()
@ -106,7 +105,7 @@ def test_pushdpopd(xession):
def test_cd_dot(xession):
xession.env = Env(PWD=os.getcwd())
xession.env.update(dict(PWD=os.getcwd()))
owd = os.getcwd().casefold()
dirstack.cd(["."])
@ -117,7 +116,7 @@ def test_cd_dot(xession):
def test_uncpushd_simple_push_pop(xession, shares_setup):
if shares_setup is None:
return
xession.env = Env(CDPATH=PARENT, PWD=HERE)
xession.env.update(dict(CDPATH=PARENT, PWD=HERE))
dirstack.cd([PARENT])
owd = os.getcwd()
assert owd.casefold() == xession.env["PWD"].casefold()
@ -134,7 +133,7 @@ def test_uncpushd_simple_push_pop(xession, shares_setup):
def test_uncpushd_push_to_same_share(xession, shares_setup):
if shares_setup is None:
return
xession.env = Env(CDPATH=PARENT, PWD=HERE)
xession.env.update(dict(CDPATH=PARENT, PWD=HERE))
dirstack.cd([PARENT])
owd = os.getcwd()
@ -168,7 +167,7 @@ def test_uncpushd_push_other_push_same(xession, shares_setup):
Then push to a again. Pop (check b unmapped and a still mapped), pop, pop (check a is unmapped)"""
if shares_setup is None:
return
xession.env = Env(CDPATH=PARENT, PWD=HERE)
xession.env.update(dict(CDPATH=PARENT, PWD=HERE))
dirstack.cd([PARENT])
owd = os.getcwd()

View file

@ -11,7 +11,6 @@ from time import sleep
import pytest
from xonsh.tools import always_true, DefaultNotGiven
from xonsh.commands_cache import CommandsCache
from xonsh.environ import (
Env,
locate_binary,
@ -168,13 +167,13 @@ def test_thread_local_swap():
success_variables[index] = False
break
with env.swap(a=index):
sleep(0.1)
sleep(0.01)
if env["a"] == index:
success_variables[index] = True
else:
success_variables[index] = False
break
sleep(0.1)
sleep(0.01)
with env.swap(a="swapped"):
threads = [
@ -200,7 +199,6 @@ def test_locate_binary_on_windows(xession):
with open(fpath, "w") as f:
f.write(fpath)
xession.env.update({"PATH": [tmpdir], "PATHEXT": [".COM", ".EXE", ".BAT"]})
xession.commands_cache = CommandsCache()
assert locate_binary("file1") == os.path.join(tmpdir, "file1.exe")
assert locate_binary("file1.exe") == os.path.join(tmpdir, "file1.exe")
assert locate_binary("file2") == os.path.join(tmpdir, "FILE2.BAT")
@ -208,9 +206,8 @@ def test_locate_binary_on_windows(xession):
assert locate_binary("file3") is None
def test_event_on_envvar_change(xession):
env = Env(TEST=0)
xession.env = env
def test_event_on_envvar_change(xession, env):
env["TEST"] = 0
share = []
# register
@ -224,9 +221,7 @@ def test_event_on_envvar_change(xession):
assert share == ["TEST", 0, 1]
def test_event_on_envvar_new(xession):
env = Env()
xession.env = env
def test_event_on_envvar_new(xession, env):
share = []
# register
@ -240,9 +235,8 @@ def test_event_on_envvar_new(xession):
assert share == ["TEST", 1]
def test_event_on_envvar_change_from_none_value(xession):
env = Env(TEST=None)
xession.env = env
def test_event_on_envvar_change_from_none_value(xession, env):
env["TEST"] = None
share = []
# register
@ -257,9 +251,8 @@ def test_event_on_envvar_change_from_none_value(xession):
@pytest.mark.parametrize("val", [1, None, True, "ok"])
def test_event_on_envvar_change_no_fire_when_value_is_same(val, xession):
env = Env(TEST=val)
xession.env = env
def test_event_on_envvar_change_no_fire_when_value_is_same(val, xession, env):
env["TEST"] = val
share = []
# register
@ -273,9 +266,7 @@ def test_event_on_envvar_change_no_fire_when_value_is_same(val, xession):
assert share == []
def test_events_on_envvar_called_in_right_order(xession):
env = Env()
xession.env = env
def test_events_on_envvar_called_in_right_order(xession, env):
share = []
# register
@ -341,7 +332,7 @@ def test_delitem_default():
assert env[a_key] == a_value
def test_lscolors_target(xonsh_builtins):
def test_lscolors_target(xession):
lsc = LsColors.fromstring("ln=target")
assert lsc["ln"] == ("RESET",)
assert lsc.is_target("ln")

View file

@ -1,82 +1,93 @@
"""Tests the xonsh lexer."""
import os
from tools import (
check_eval,
check_exec,
check_parse,
skip_if_on_unix,
skip_if_on_windows,
)
from tools import skip_if_on_unix, skip_if_on_windows, ON_WINDOWS
import pytest
@pytest.fixture(autouse=True)
def xonsh_execer_autouse(xonsh_builtins, xonsh_execer):
return xonsh_execer
@pytest.fixture
def check_eval(xonsh_execer, xonsh_session, monkeypatch):
def factory(input):
env = {
"AUTO_CD": False,
"XONSH_ENCODING": "utf-8",
"XONSH_ENCODING_ERRORS": "strict",
"PATH": [],
}
for key, val in env.items():
monkeypatch.setitem(xonsh_session.env, key, val)
if ON_WINDOWS:
monkeypatch.setitem(
xonsh_session.env, "PATHEXT", [".COM", ".EXE", ".BAT", ".CMD"]
)
xonsh_execer.eval(input)
return True
return factory
@skip_if_on_unix
def test_win_ipconfig():
def test_win_ipconfig(check_eval):
assert check_eval(os.environ["SYSTEMROOT"] + "\\System32\\ipconfig.exe /all")
@skip_if_on_unix
def test_ipconfig():
def test_ipconfig(check_eval):
assert check_eval("ipconfig /all")
@skip_if_on_windows
def test_bin_ls():
def test_bin_ls(check_eval):
assert check_eval("/bin/ls -l")
def test_ls_dashl():
assert check_parse("ls -l")
def test_ls_dashl(xonsh_execer_parse):
assert xonsh_execer_parse("ls -l")
def test_which_ls():
assert check_parse("which ls")
def test_which_ls(xonsh_execer_parse):
assert xonsh_execer_parse("which ls")
def test_echo_hello():
assert check_parse("echo hello")
def test_echo_hello(xonsh_execer_parse):
assert xonsh_execer_parse("echo hello")
def test_echo_star_with_semi():
assert check_parse("echo * spam ; ![echo eggs]\n")
def test_echo_star_with_semi(xonsh_execer_parse):
assert xonsh_execer_parse("echo * spam ; ![echo eggs]\n")
def test_simple_func():
def test_simple_func(xonsh_execer_parse):
code = "def prompt():\n" " return '{user}'.format(user='me')\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_lookup_alias():
def test_lookup_alias(xonsh_execer_parse):
code = "def foo(a, s=None):\n" ' return "bar"\n' "@(foo)\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_lookup_anon_alias():
def test_lookup_anon_alias(xonsh_execer_parse):
code = 'echo "hi" | @(lambda a, s=None: a[0]) foo bar baz\n'
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_simple_func_broken():
def test_simple_func_broken(xonsh_execer_parse):
code = "def prompt():\n" " return '{user}'.format(\n" " user='me')\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_bad_indent():
def test_bad_indent(xonsh_execer_parse):
code = "if True:\n" "x = 1\n"
with pytest.raises(SyntaxError):
check_parse(code)
xonsh_execer_parse(code)
def test_comment_colon_ending():
def test_comment_colon_ending(xonsh_execer_parse):
code = "# this is a comment:\necho hello"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_good_rhs_subproc():
@ -85,56 +96,56 @@ def test_good_rhs_subproc():
assert code
def test_bad_rhs_subproc():
def test_bad_rhs_subproc(xonsh_execer_parse):
# nonsense but unparsable
code = "str().split() | grep exit\n"
with pytest.raises(SyntaxError):
check_parse(code)
xonsh_execer_parse(code)
def test_indent_with_empty_line():
def test_indent_with_empty_line(xonsh_execer_parse):
code = "if True:\n" "\n" " some_command for_sub_process_mode\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_command_in_func():
def test_command_in_func(xonsh_execer_parse):
code = "def f():\n" " echo hello\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_command_in_func_with_comment():
def test_command_in_func_with_comment(xonsh_execer_parse):
code = "def f():\n" " echo hello # comment\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_pyeval_redirect():
def test_pyeval_redirect(xonsh_execer_parse):
code = 'echo @("foo") > bar\n'
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_pyeval_multiline_str():
def test_pyeval_multiline_str(xonsh_execer_parse):
code = 'echo @("""hello\nmom""")\n'
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_echo_comma():
def test_echo_comma(xonsh_execer_parse):
code = "echo ,\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_echo_comma_val():
def test_echo_comma_val(xonsh_execer_parse):
code = "echo ,1\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_echo_comma_2val():
def test_echo_comma_2val(xonsh_execer_parse):
code = "echo 1,2\n"
assert check_parse(code)
assert xonsh_execer_parse(code)
def test_echo_line_cont():
def test_echo_line_cont(xonsh_execer_parse):
code = 'echo "1 \\\n2"\n'
assert check_parse(code)
assert xonsh_execer_parse(code)
@pytest.mark.parametrize(
@ -146,40 +157,40 @@ def test_echo_line_cont():
"if True:\\\n echo a \\\n b\n",
],
)
def test_two_echo_line_cont(code):
assert check_parse(code)
def test_two_echo_line_cont(code, xonsh_execer_parse):
assert xonsh_execer_parse(code)
def test_eval_eol():
def test_eval_eol(check_eval):
assert check_eval("0") and check_eval("0\n")
def test_annotated_assign():
def test_annotated_assign(xonsh_execer_exec):
# issue #3959 - didn't work because of `CtxAwareTransformer`
assert check_exec("x : int = 42")
assert xonsh_execer_exec("x : int = 42")
def test_exec_eol():
def test_exec_eol(xonsh_execer_exec):
locs = dict()
assert check_exec("a=0", locs=locs) and check_exec("a=0\n", locs=locs)
assert xonsh_execer_exec("a=0", locs=locs) and xonsh_execer_exec("a=0\n", locs=locs)
def test_exec_print(capsys):
def test_exec_print(capsys, xonsh_execer_exec):
ls = {"nested": "some long list"}
check_exec("print(ls)", locs=dict(ls=ls))
xonsh_execer_exec("print(ls)", locs=dict(ls=ls))
out, err = capsys.readouterr()
assert out.strip() == repr(ls)
def test_exec_function_scope():
def test_exec_function_scope(xonsh_execer_exec):
# issue 4363
assert check_exec("x = 0; (lambda: x)()")
assert check_exec("x = 0; [x for _ in [0]]")
assert xonsh_execer_exec("x = 0; (lambda: x)()")
assert xonsh_execer_exec("x = 0; [x for _ in [0]]")
def test_exec_scope_reuse():
def test_exec_scope_reuse(xonsh_execer_exec):
# Scopes should not be reused between execs.
# A first-pass incorrect solution to issue 4363 made this mistake.
assert check_exec("x = 0")
assert xonsh_execer_exec("x = 0")
with pytest.raises(NameError):
check_exec("print(x)")
xonsh_execer_exec("print(x)")

View file

@ -5,16 +5,13 @@ from importlib import import_module
import pytest
from xonsh import imphooks
from xonsh.environ import Env
from xonsh.built_ins import XSH
@pytest.fixture(autouse=True)
def imp_env(xession):
xession.env = Env({"PATH": [], "PATHEXT": []})
xession.env.update({"PATH": [], "PATHEXT": []})
imphooks.install_import_hooks(xession.execer)
yield
XSH.unload()
def test_import():

View file

@ -423,7 +423,7 @@ def test_xonsh_failback(
rc_shells,
exp_shell,
shell,
xonsh_builtins,
xession,
monkeypatch,
monkeypatch_stderr,
):

File diff suppressed because it is too large Load diff

View file

@ -8,22 +8,23 @@ import pytest
from xonsh.platform import ON_WINDOWS
from xonsh.procs.pipelines import CommandPipeline
from tests.tools import skip_if_on_windows, skip_if_on_unix
from xonsh.built_ins import XSH
@pytest.fixture(autouse=True)
def patched_events(monkeypatch, xonsh_events, xonsh_execer):
def patched_events(monkeypatch, xonsh_events, xonsh_session):
from xonsh.jobs import tasks
tasks.clear()
# needed for ci tests
monkeypatch.setitem(
XSH.env, "RAISE_SUBPROC_ERROR", False
xonsh_session.env, "RAISE_SUBPROC_ERROR", False
) # for the failing `grep` commands
monkeypatch.setitem(XSH.env, "XONSH_CAPTURE_ALWAYS", True) # capture output of ![]
monkeypatch.setitem(
xonsh_session.env, "XONSH_CAPTURE_ALWAYS", True
) # capture output of ![]
if ON_WINDOWS:
monkeypatch.setattr(
XSH,
xonsh_session,
"aliases",
{
"echo": "cmd /c echo".split(),

View file

@ -1,6 +1,5 @@
"""Test XonshLexer for pygments"""
import gc
import pytest
from pygments.token import (
@ -16,55 +15,51 @@ from pygments.token import (
)
from tools import skip_if_on_windows
from xonsh.platform import ON_WINDOWS
from xonsh.built_ins import XSH
from xonsh.pyghooks import XonshLexer, Color, XonshStyle, on_lscolors_change
from xonsh.environ import LsColors
from xonsh.events import events, EventManager
from tools import DummyShell
@pytest.fixture(autouse=True)
def load_command_cache(xession):
gc.collect()
XSH.unload()
XSH.load()
if ON_WINDOWS:
for key in ("cd", "bash"):
xession.aliases[key] = lambda *args, **kwargs: None
@pytest.fixture
def xsh(xession, monkeypatch):
for key in ("cd", "bash"):
monkeypatch.setitem(xession.aliases, key, lambda *args, **kwargs: None)
def check_token(code, tokens):
"""Make sure that all tokens appears in code in order"""
lx = XonshLexer()
tks = list(lx.get_tokens(code))
@pytest.fixture()
def check_token(xsh):
def factory(code, tokens):
"""Make sure that all tokens appears in code in order"""
lx = XonshLexer()
tks = list(lx.get_tokens(code))
for tk in tokens:
while tks:
if tk == tks[0]:
for tk in tokens:
while tks:
if tk == tks[0]:
break
tks = tks[1:]
else:
msg = f"Token {tk!r} missing: {list(lx.get_tokens(code))!r}"
pytest.fail(msg)
break
tks = tks[1:]
else:
msg = f"Token {tk!r} missing: {list(lx.get_tokens(code))!r}"
pytest.fail(msg)
break
return factory
@skip_if_on_windows
def test_ls():
check_token("ls -al", [(Name.Builtin, "ls")])
@skip_if_on_windows
def test_bin_ls():
check_token("/bin/ls -al", [(Name.Builtin, "/bin/ls")])
@skip_if_on_windows
def test_py_print():
check_token(
'print("hello")',
[
_cases = {
"ls": {
"ls -al": [
(Name.Builtin, "ls"),
],
},
"ls-bin": {
"/bin/ls -al": [
(Name.Builtin, "/bin/ls"),
],
},
"print": {
'print("hello")': [
(Name.Builtin, "print"),
(Punctuation, "("),
(Literal.String.Double, '"'),
@ -72,46 +67,44 @@ def test_py_print():
(Literal.String.Double, '"'),
(Punctuation, ")"),
(Text, "\n"),
]
},
"invalid-cmd": {
"non-existance-cmd -al": [
(Name, "non"),
],
)
@skip_if_on_windows
def test_invalid_cmd():
check_token("non-existance-cmd -al", [(Name, "non")]) # parse as python
check_token(
"![non-existance-cmd -al]", [(Error, "non-existance-cmd")]
) # parse as error
check_token("for i in range(10):", [(Keyword, "for")]) # as py keyword
check_token("(1, )", [(Punctuation, "("), (Number.Integer, "1")])
@skip_if_on_windows
def test_multi_cmd():
check_token(
"cd && cd", [(Name.Builtin, "cd"), (Operator, "&&"), (Name.Builtin, "cd")]
)
check_token(
"cd || non-existance-cmd",
[(Name.Builtin, "cd"), (Operator, "||"), (Error, "non-existance-cmd")],
)
@skip_if_on_windows
def test_nested():
check_token(
'echo @("hello")',
[
"![non-existance-cmd -al]": [
(Error, "non-existance-cmd"),
],
"for i in range(10):": [
(Keyword, "for"),
],
"(1, )": [
(Punctuation, "("),
(Number.Integer, "1"),
],
},
"multi-cmd": {
"cd && cd": [
(Name.Builtin, "cd"),
(Operator, "&&"),
(Name.Builtin, "cd"),
],
"cd || non-existance-cmd": [
(Name.Builtin, "cd"),
(Operator, "||"),
(Error, "non-existance-cmd"),
],
},
"nested": {
'echo @("hello")': [
(Name.Builtin, "echo"),
(Keyword, "@"),
(Punctuation, "("),
(String.Double, "hello"),
(Punctuation, ")"),
],
)
check_token(
"print($(cd))",
[
"print($(cd))": [
(Name.Builtin, "print"),
(Punctuation, "("),
(Keyword, "$"),
@ -121,10 +114,7 @@ def test_nested():
(Punctuation, ")"),
(Text, "\n"),
],
)
check_token(
r'print(![echo "])\""])',
[
r'print(![echo "])\""])': [
(Name.Builtin, "print"),
(Punctuation, "("),
(Keyword, "!"),
@ -136,7 +126,53 @@ def test_nested():
(Punctuation, ")"),
(Text, "\n"),
],
)
},
"subproc-args": {
"cd 192.168.0.1": [
(Text, "192.168.0.1"),
],
},
"backtick": {
r"echo g`.*\w+`": [
(String.Affix, "g"),
(String.Backtick, "`"),
(String.Regex, "."),
(String.Regex, "*"),
(String.Escape, r"\w"),
],
},
"macro": {
r"g!(42, *, 65)": [
(Name, "g"),
(Keyword, "!"),
(Punctuation, "("),
(Number.Integer, "42"),
],
r"echo! hello world": [
(Name.Builtin, "echo"),
(Keyword, "!"),
(String, "hello world"),
],
r"bash -c ! export var=42; echo $var": [
(Name.Builtin, "bash"),
(Text, "-c"),
(Keyword, "!"),
(String, "export var=42; echo $var"),
],
},
}
def _convert_cases():
for title, input_dict in _cases.items():
for idx, item in enumerate(input_dict.items()):
yield pytest.param(*item, id=f"{title}-{idx}")
@pytest.mark.parametrize("inp, expected", list(_convert_cases()))
@skip_if_on_windows
def test_xonsh_lexer(inp, expected, check_token):
check_token(inp, expected)
# can't seem to get thie test to import pyghooks and define on_lscolors_change handler like live code does.
@ -160,8 +196,7 @@ def xonsh_builtins_ls_colors(xession, events_fxt):
@skip_if_on_windows
def test_path(tmpdir, xonsh_builtins_ls_colors):
def test_path(tmpdir, xonsh_builtins_ls_colors, check_token):
test_dir = str(tmpdir.mkdir("xonsh-test-highlight-path"))
check_token(f"cd {test_dir}", [(Name.Builtin, "cd"), (Color.BOLD_BLUE, test_dir)])
check_token(
@ -175,7 +210,7 @@ def test_path(tmpdir, xonsh_builtins_ls_colors):
@skip_if_on_windows
def test_color_on_lscolors_change(tmpdir, xonsh_builtins_ls_colors):
def test_color_on_lscolors_change(tmpdir, xonsh_builtins_ls_colors, check_token):
"""Verify colorizer returns Token.Text if file type not defined in LS_COLORS"""
lsc = xonsh_builtins_ls_colors.env["LS_COLORS"]
@ -188,43 +223,3 @@ def test_color_on_lscolors_change(tmpdir, xonsh_builtins_ls_colors):
del lsc["di"]
check_token(f"cd {test_dir}", [(Name.Builtin, "cd"), (Text, test_dir)])
@skip_if_on_windows
def test_subproc_args():
check_token("cd 192.168.0.1", [(Text, "192.168.0.1")])
@skip_if_on_windows
def test_backtick():
check_token(
r"echo g`.*\w+`",
[
(String.Affix, "g"),
(String.Backtick, "`"),
(String.Regex, "."),
(String.Regex, "*"),
(String.Escape, r"\w"),
],
)
@skip_if_on_windows
def test_macro():
check_token(
r"g!(42, *, 65)",
[(Name, "g"), (Keyword, "!"), (Punctuation, "("), (Number.Integer, "42")],
)
check_token(
r"echo! hello world",
[(Name.Builtin, "echo"), (Keyword, "!"), (String, "hello world")],
)
check_token(
r"bash -c ! export var=42; echo $var",
[
(Name.Builtin, "bash"),
(Text, "-c"),
(Keyword, "!"),
(String, "export var=42; echo $var"),
],
)

View file

@ -32,7 +32,7 @@ def test_prompt_toolkit_version_checks(
exp_shell_type,
warn_snip,
monkeypatch,
xonsh_builtins,
xession,
):
mocked_warn = ""

View file

@ -23,18 +23,24 @@ from xonsh.environ import LsColors
@pytest.fixture
def xs_LS_COLORS(xession):
def xs_LS_COLORS(xession, os_env, monkeypatch):
"""Xonsh environment including LS_COLORS"""
e = xession.env
# original env is needed on windows. since it will skip enhanced coloring
# for some emulators
monkeypatch.setattr(xession, "env", os_env)
lsc = LsColors(LsColors.default_settings)
xession.env["LS_COLORS"] = lsc
# todo: a separate test for this as True
xession.env["INTENSIFY_COLORS_ON_WIN"] = False
xession.shell.shell_type = "prompt_toolkit"
xession.shell.shell.styler = XonshStyle() # default style
yield xession
xession.env = e
DEFAULT_STYLES = {
# Reset

View file

@ -1,7 +1,6 @@
"""Testing for ``xonsh.shell.Shell``"""
import os
from xonsh.environ import Env
from xonsh.shell import Shell
from xonsh.history.json import JsonHistory
from xonsh.history.sqlite import SqliteHistory
@ -32,12 +31,14 @@ def test_shell_with_json_history(xession, xonsh_execer, tmpdir_factory):
)
h.flush()
xession.env = Env(
XONSH_DATA_DIR=tempdir,
XONSH_INTERACTIVE=True,
XONSH_HISTORY_BACKEND="json",
XONSH_HISTORY_FILE=history_file,
# XONSH_DEBUG=1 # to show errors
xession.env.update(
dict(
XONSH_DATA_DIR=tempdir,
XONSH_INTERACTIVE=True,
XONSH_HISTORY_BACKEND="json",
XONSH_HISTORY_FILE=history_file,
# XONSH_DEBUG=1 # to show errors
)
)
Shell(xonsh_execer, shell_type="none")
@ -69,12 +70,14 @@ def test_shell_with_sqlite_history(xession, xonsh_execer, tmpdir_factory):
)
h.flush()
xession.env = Env(
XONSH_DATA_DIR=tempdir,
XONSH_INTERACTIVE=True,
XONSH_HISTORY_BACKEND="sqlite",
XONSH_HISTORY_FILE=history_file,
# XONSH_DEBUG=1 # to show errors
xession.env.update(
dict(
XONSH_DATA_DIR=tempdir,
XONSH_INTERACTIVE=True,
XONSH_HISTORY_BACKEND="sqlite",
XONSH_HISTORY_FILE=history_file,
# XONSH_DEBUG=1 # to show errors
)
)
Shell(xonsh_execer, shell_type="none")
@ -86,7 +89,7 @@ def test_shell_with_dummy_history_in_not_interactive(xession, xonsh_execer):
"""
Check that shell use Dummy history in not interactive mode.
"""
xession.env = Env(XONSH_INTERACTIVE=False)
xession.env["XONSH_INTERACTIVE"] = False
xession.history = None
Shell(xonsh_execer, shell_type="none")
assert isinstance(xession.history, DummyHistory)

View file

@ -90,7 +90,6 @@ from xonsh.tools import (
expand_case_matching,
expandvars,
)
from xonsh.environ import Env
from tools import skip_if_on_windows
@ -536,7 +535,7 @@ mom"""
@pytest.mark.parametrize("src, idx, exp_line, exp_n", LOGICAL_LINE_CASES)
def test_get_logical_line(src, idx, exp_line, exp_n, xonsh_builtins):
def test_get_logical_line(src, idx, exp_line, exp_n, xession):
lines = src.splitlines()
line, n, start = get_logical_line(lines, idx)
assert exp_line == line
@ -544,7 +543,7 @@ def test_get_logical_line(src, idx, exp_line, exp_n, xonsh_builtins):
@pytest.mark.parametrize("src, idx, exp_line, exp_n", LOGICAL_LINE_CASES)
def test_replace_logical_line(src, idx, exp_line, exp_n, xonsh_builtins):
def test_replace_logical_line(src, idx, exp_line, exp_n, xession):
lines = src.splitlines()
logical = exp_line
while idx > 0 and lines[idx - 1].endswith("\\"):
@ -1651,10 +1650,9 @@ def test_expand_case_matching(inp, exp):
)
def test_expandvars(inp, exp, xession):
"""Tweaked for xonsh cases from CPython `test_genericpath.py`"""
env = Env(
{"foo": "bar", "spam": "eggs", "a_bool": True, "an_int": 42, "none": None}
xession.env.update(
dict({"foo": "bar", "spam": "eggs", "a_bool": True, "an_int": 42, "none": None})
)
xession.env = env
assert expandvars(inp) == exp
@ -1699,9 +1697,8 @@ def test_expand_path(expand_user, inp, expand_env_vars, exp_end, xession):
inp = inp.replace("/", os.sep)
exp_end = exp_end.replace("/", os.sep)
env = Env({"foo": "bar", "a_bool": True, "an_int": 42, "none": None})
env["EXPAND_ENV_VARS"] = expand_env_vars
xession.env = env
xession.env.update({"foo": "bar", "a_bool": True, "an_int": 42, "none": None})
xession.env["EXPAND_ENV_VARS"] = expand_env_vars
path = expand_path(inp, expand_user=expand_user)

View file

@ -99,7 +99,7 @@ def test_activate_non_vox_venv(xession, vox, record_events, tmpdir):
Create a virtual environment using Python's built-in venv module
(not in VIRTUALENV_HOME) and verify that vox can activate it correctly.
"""
xession.env.setdefault("PATH", [])
xession.env["PATH"] = []
record_events("vox_on_activate", "vox_on_deactivate")
@ -286,9 +286,8 @@ def patched_cmd_cache(xession, vox, venvs, monkeypatch):
def no_change(self, *_):
return False, False, False
monkeypatch.setattr(cc, "_update_if_changed", types.MethodType(no_change, cc))
monkeypatch.setattr(cc, "_check_changes", types.MethodType(no_change, cc))
monkeypatch.setattr(cc, "_update_cmds_cache", types.MethodType(no_change, cc))
monkeypatch.setattr(cc, "cache_file", None)
bins = {path: (path, False) for path in _PY_BINS}
cc._cmds_cache.update(bins)
yield cc

View file

@ -15,7 +15,7 @@ from xonsh.tools import ON_WINDOWS
from xonsh.xonfig import xonfig_main
def test_xonfg_help(capsys, xonsh_builtins):
def test_xonfg_help(capsys, xession):
"""verify can invoke it, and usage knows about all the options"""
with pytest.raises(SystemExit):
xonfig_main(["-h"])
@ -46,7 +46,7 @@ def test_xonfg_help(capsys, xonsh_builtins):
),
], # NOQA E231
)
def test_xonfig_info(args, xonsh_builtins):
def test_xonfig_info(args, xession):
"""info works, and reports no jupyter if none in environment"""
capout = xonfig_main(args)
assert capout.startswith("+---")
@ -89,7 +89,7 @@ def fake_lib(monkeypatch):
del sys.modules[m]
def test_xonfig_kernel_with_jupyter(monkeypatch, capsys, fake_lib, xonsh_builtins):
def test_xonfig_kernel_with_jupyter(monkeypatch, capsys, fake_lib, xession):
cap_args = None
cap_spec = None
@ -129,6 +129,6 @@ def test_xonfig_kernel_with_jupyter(monkeypatch, capsys, fake_lib, xonsh_builtin
assert cap_spec["argv"][2] == "xonsh.jupyter_kernel"
def test_xonfig_kernel_no_jupyter(capsys, xonsh_builtins):
def test_xonfig_kernel_no_jupyter(capsys, xession):
with pytest.raises(ImportError):
rc = xonfig_main(["jupyter-kernel"]) # noqa F841

View file

@ -1,17 +1,15 @@
"""Tests the xonsh lexer."""
import copy
import os
import sys
import ast
import platform
import subprocess
import contextlib
import threading
from collections import defaultdict
from collections.abc import MutableMapping
import pytest
from xonsh.built_ins import XSH
from xonsh.environ import Env
from xonsh.base_shell import BaseShell
@ -24,9 +22,6 @@ ON_CONDA = True in [
conda in pytest.__file__.lower() for conda in ["conda", "anaconda", "miniconda"]
]
ON_TRAVIS = "TRAVIS" in os.environ and "CI" in os.environ
ON_AZURE_PIPELINES = os.environ.get("TF_BUILD", "") == "True"
print("ON_AZURE_PIPELINES", repr(ON_AZURE_PIPELINES))
print("os.environ['TF_BUILD']", repr(os.environ.get("TF_BUILD", "")))
TEST_DIR = os.path.dirname(__file__)
# pytest skip decorators
@ -40,10 +35,6 @@ skip_if_on_msys = pytest.mark.skipif(
skip_if_on_windows = pytest.mark.skipif(ON_WINDOWS, reason="Unix stuff")
skip_if_on_azure_pipelines = pytest.mark.skipif(
ON_AZURE_PIPELINES, reason="not suitable for azure"
)
skip_if_on_unix = pytest.mark.skipif(not ON_WINDOWS, reason="Windows stuff")
skip_if_on_darwin = pytest.mark.skipif(ON_DARWIN, reason="not Mac friendly")
@ -94,103 +85,6 @@ class DummyHistory:
pass
class DummyEnv(MutableMapping):
DEFAULTS = {
"XONSH_DEBUG": 1,
"XONSH_COLOR_STYLE": "default",
"VC_BRANCH_TIMEOUT": 1,
}
def __init__(self, *args, **kwargs):
self._d = self.DEFAULTS.copy()
self._d.update(dict(*args, **kwargs))
def detype(self):
return {k: str(v) for k, v in self._d.items()}
def __getitem__(self, k):
if k is ...:
return self
else:
return self._d[k]
def __setitem__(self, k, v):
assert k is not ...
self._d[k] = v
def __delitem__(self, k):
assert k is not ...
del self._d[k]
def __len__(self):
return len(self._d)
def __iter__(self):
yield from self._d
@contextlib.contextmanager
def swap(self, other=None, **kwargs):
old = {}
# single positional argument should be a dict-like object
if other is not None:
for k, v in other.items():
old[k] = self.get(k, NotImplemented)
self[k] = v
# kwargs could also have been sent in
for k, v in kwargs.items():
old[k] = self.get(k, NotImplemented)
self[k] = v
yield self
# restore the values
for k, v in old.items():
if v is NotImplemented:
del self[k]
else:
self[k] = v
@staticmethod
def get_swapped_values():
return {}
@staticmethod
def set_swapped_values(_):
pass
def is_manually_set(self, key):
return False
#
# Execer tools
#
def check_exec(input, **kwargs):
XSH.execer.exec(input, **kwargs)
return True
def check_eval(input):
XSH.env = Env(
{
"AUTO_CD": False,
"XONSH_ENCODING": "utf-8",
"XONSH_ENCODING_ERRORS": "strict",
"PATH": [],
}
)
if ON_WINDOWS:
XSH.env["PATHEXT"] = [".COM", ".EXE", ".BAT", ".CMD"]
XSH.execer.eval(input)
return True
def check_parse(input):
tree = XSH.execer.parse(input, ctx=None)
return tree
#
# Parser tools
#
@ -245,3 +139,17 @@ def completions_from_result(results):
if results is None:
return set()
return results
def copy_env(old):
from xonsh.environ import Env, InternalEnvironDict
env: Env = copy.copy(old)
internal = InternalEnvironDict()
internal._global = env._d._global.copy()
internal._thread_local = threading.local()
env._d = internal
env._vars = env._vars.copy()
env._detyped = None
return env

View file

@ -5,6 +5,7 @@ A background predictor is a function that accepts a single argument list
and returns whether or not the process can be run in the background (returns
True) or must be run the foreground (returns False).
"""
import functools
import os
import pickle
import sys
@ -39,15 +40,24 @@ class CommandsCache(cabc.Mapping):
self.threadable_predictors = default_threadable_predictors()
self._loaded_pickled = False
# Path to the cache-file where all commands/aliases are cached for pre-loading"""
env = XSH.env
self.cache_file = (
(Path(env["XONSH_DATA_DIR"]).joinpath(self.CACHE_FILE).resolve())
if env is not None
and "XONSH_DATA_DIR" in env
and env.get("COMMANDS_CACHE_SAVE_INTERMEDIATE")
else None
)
# force it to load from env by setting it to None
self._cache_file = None
@property
def cache_file(self):
"""Keeping a property that lies on instance-attribute"""
env = XSH.env or {}
# Path to the cache-file where all commands/aliases are cached for pre-loading
if self._cache_file is None:
if "XONSH_DATA_DIR" in env and env.get("COMMANDS_CACHE_SAVE_INTERMEDIATE"):
self._cache_file = (
Path(env["XONSH_DATA_DIR"]).joinpath(self.CACHE_FILE).resolve()
)
else:
# set a falsy value other than None
self._cache_file = ""
return self._cache_file
def __contains__(self, key):
self.update_cache()
@ -98,7 +108,7 @@ class CommandsCache(cabc.Mapping):
if os.path.isdir(p):
yield p
def _update_if_changed(self, paths: tp.Tuple[str, ...], aliases):
def _check_changes(self, paths: tp.Tuple[str, ...], aliases):
# did PATH change?
path_hash = hash(paths)
yield path_hash == self._path_checksum
@ -120,20 +130,20 @@ class CommandsCache(cabc.Mapping):
return self._cmds_cache
def update_cache(self):
env = XSH.env
path = [] if env is None else XSH.env.get("PATH", [])
path_immut = tuple(CommandsCache.remove_dups(path))
env = XSH.env or {}
paths = tuple(CommandsCache.remove_dups(env.get("PATH") or []))
# in case it is empty or unset
alss = {} if XSH.aliases is None else XSH.aliases
(
has_path_changed,
has_alias_changed,
has_any_path_updated,
) = tuple(self._update_if_changed(path_immut, alss))
if has_path_changed and has_any_path_updated:
if not has_alias_changed:
no_new_paths,
no_new_alias,
no_new_bins,
) = tuple(self._check_changes(paths, alss))
if no_new_paths and no_new_bins:
if not no_new_alias: # only aliases have changed
for cmd, alias in alss.items():
key = cmd.upper() if ON_WINDOWS else cmd
if key in self._cmds_cache:
@ -153,26 +163,41 @@ class CommandsCache(cabc.Mapping):
# also start a thread that updates the cache in the bg
worker = threading.Thread(
target=self._update_cmds_cache,
args=[path_immut, alss],
args=[paths, alss],
daemon=True,
)
worker.start()
else:
self._update_cmds_cache(path_immut, alss)
self._update_cmds_cache(paths, alss)
return self._cmds_cache
@staticmethod
@functools.lru_cache(maxsize=10)
def _get_all_cmds(paths: tp.Sequence[str]):
"""Cache results when possible
This will be helpful especially during tests where the PATH will be the same mostly.
"""
def _getter():
for path in reversed(paths):
# iterate backwards so that entries at the front of PATH overwrite
# entries at the back.
for cmd in executables_in(path):
yield cmd, os.path.join(path, cmd)
return dict(_getter())
def _update_cmds_cache(
self, paths: tp.Sequence[str], aliases: tp.Dict[str, str]
) -> tp.Dict[str, tp.Any]:
"""Update the cmds_cache variable in background without slowing down parseLexer"""
env = XSH.env or {} # type: ignore
allcmds = {}
for path in reversed(paths):
# iterate backwards so that entries at the front of PATH overwrite
# entries at the back.
for cmd in executables_in(path):
key = cmd.upper() if ON_WINDOWS else cmd
allcmds[key] = (os.path.join(path, cmd), aliases.get(key, None))
for cmd, path in self._get_all_cmds(paths).items():
key = cmd.upper() if ON_WINDOWS else cmd
allcmds[key] = (path, aliases.get(key, None))
warn_cnt = env.get("COMMANDS_CACHE_SIZE_WARNING")
if warn_cnt and len(allcmds) > warn_cnt:
@ -185,6 +210,7 @@ class CommandsCache(cabc.Mapping):
if cmd not in allcmds:
key = cmd.upper() if ON_WINDOWS else cmd
allcmds[key] = (cmd, True) # type: ignore
return self.set_cmds_cache(allcmds)
def get_cached_commands(self) -> tp.Dict[str, str]:

View file

@ -26,7 +26,6 @@ class Execer:
filename="<xonsh-code>",
debug_level=0,
parser_args=None,
unload=True,
scriptcache=True,
cacheall=False,
):
@ -50,7 +49,6 @@ class Execer:
self.filename = filename
self._default_filename = filename
self.debug_level = debug_level
self.unload = unload
self.scriptcache = scriptcache
self.cacheall = cacheall
self.ctxtransformer = CtxAwareTransformer(self.parser)

View file

@ -8,21 +8,16 @@ import xonsh.platform as xp
from xonsh.built_ins import XSH
def _replace_home(x):
if xp.ON_WINDOWS:
home = XSH.env["HOMEDRIVE"] + XSH.env["HOMEPATH"][0]
if x.startswith(home):
x = x.replace(home, "~", 1)
def _replace_home(x: str):
home = os.path.expanduser("~")
if x.startswith(home):
x = x.replace(home, "~", 1)
if XSH.env.get("FORCE_POSIX_PATHS"):
if xp.ON_WINDOWS:
if XSH.env.get("FORCE_POSIX_PATHS") and os.altsep:
x = x.replace(os.sep, os.altsep)
return x
else:
home = XSH.env["HOME"]
if x.startswith(home):
x = x.replace(home, "~", 1)
return x
return x
def _replace_home_cwd():

View file

@ -2,7 +2,6 @@
import os
import re
import sys
import builtins
import stat
from collections import ChainMap
from collections.abc import MutableMapping
@ -268,7 +267,7 @@ def partial_color_tokenize(template):
These sub-strings maybe templates themselves.
"""
if XSH.shell is not None:
styles = __xonsh__.shell.shell.styler.styles
styles = XSH.shell.shell.styler.styles
else:
styles = None
color = Color.DEFAULT
@ -1651,16 +1650,12 @@ class XonshLexer(Python3Lexer):
def __init__(self, *args, **kwargs):
# If the lexer is loaded as a pygment plugin, we have to mock
# __xonsh__.env and __xonsh__.commands_cache
if not hasattr(builtins, "__xonsh__"):
from argparse import Namespace
builtins.__xonsh__ = Namespace()
if not hasattr(XSH, "env"):
XSH.env = {}
if ON_WINDOWS:
pathext = os_environ.get("PATHEXT", [".EXE", ".BAT", ".CMD"])
XSH.env["PATHEXT"] = pathext.split(os.pathsep)
if not getattr(XSH, "commands_cache", None):
if getattr(XSH, "commands_cache", None) is None:
XSH.commands_cache = CommandsCache()
_ = XSH.commands_cache.all_commands # NOQA
super().__init__(*args, **kwargs)

View file

@ -541,10 +541,12 @@ def get_line_continuation():
mode on Windows the backslash must be preceded by a space. This is because
paths on Windows may end in a backslash.
"""
if ON_WINDOWS and hasattr(xsh, "env") and xsh.env.get("XONSH_INTERACTIVE", False):
return " \\"
else:
return "\\"
if ON_WINDOWS:
env = getattr(xsh, "env", None) or {}
if env.get("XONSH_INTERACTIVE", False):
return " \\"
return "\\"
def get_logical_line(lines, idx):

View file

@ -434,7 +434,7 @@ class Vox(collections.abc.Mapping):
def _get_vox_default_interpreter():
"""Return the interpreter set by the $VOX_DEFAULT_INTERPRETER if set else sys.executable"""
default = "python"
default = "python3"
if default in XSH.commands_cache:
default = XSH.commands_cache.locate_binary(default)
else: