"""Xonsh AST tests.""" import ast as pyast import pytest from xonsh import ast from xonsh.ast import BinOp, Call, Name, Store, Tuple, isexpression, min_line from xonsh.pytest.tools import nodes_equal @pytest.fixture(autouse=True) def xonsh_execer_autouse(xonsh_execer): return xonsh_execer def test_gather_names_name(): node = Name(id="y", ctx=Store()) exp = {"y"} obs = ast.gather_names(node) assert exp == obs def test_gather_names_tuple(): node = Tuple(elts=[Name(id="y", ctx=Store()), Name(id="z", ctx=Store())]) exp = {"y", "z"} obs = ast.gather_names(node) assert exp == obs def test_gather_load_store_names_tuple(): node = Tuple(elts=[Name(id="y", ctx=Store()), Name(id="z", ctx=Store())]) lexp = set() sexp = {"y", "z"} lobs, sobs = ast.gather_load_store_names(node) assert lexp == lobs assert sexp == sobs @pytest.mark.parametrize( "line1", [ "x = 1", # Both, ls and l remain undefined. "ls = 1", # l remains undefined. "l = 1", # ls remains undefined. ], ) 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 = xonsh_execer_parse(code) lsnode = tree.body[1] assert 2 == min_line(lsnode) assert isinstance(lsnode.value, Call) 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 = xonsh_execer_parse(code) lsnode = tree.body[2] assert 3 == min_line(lsnode) assert isinstance(lsnode.value, BinOp) @pytest.mark.parametrize( "inp", [ """def f(): if True: pass """, """def f(x): if x: pass """, """def f(*args): if not args: pass """, """def f(*, y): if y: pass """, """def f(**kwargs): if not kwargs: pass """, """def f(k=42): if not k: pass """, """def f(k=10, *, a, b=1, **kw): if not kw and b: pass """, """import os path = '/path/to/wakka' paths = [] for root, dirs, files in os.walk(path): paths.extend(os.path.join(root, d) for d in dirs) paths.extend(os.path.join(root, f) for f in files) """, """lambda x: x + 1 """, """def f(x): return [i for i in x if i is not None and i < 10] """, ], ) def test_unmodified(inp, xonsh_execer_parse): # Context sensitive parsing should not modify AST exp = pyast.parse(inp) obs = xonsh_execer_parse(inp) assert nodes_equal(exp, obs) @pytest.mark.parametrize( "test_input", ["echo; echo && echo\n", "echo; echo && echo a\n", "true && false && true\n"], ) def test_whitespace_subproc(test_input, xonsh_execer_parse): assert xonsh_execer_parse(test_input) @pytest.mark.parametrize( "inp,exp", [ ("1+1", True), ("1+1;", True), ("1+1\n", True), ("1+1; 2+2", False), ("1+1; 2+2;", False), ("1+1; 2+2\n", False), ("1+1; 2+2;\n", False), ("x = 42", False), ], ) def test_isexpression(xonsh_execer, inp, exp): obs = isexpression(inp) assert exp is obs