From 5d64d4492f00f646da547e327e2b3cda40200e4a Mon Sep 17 00:00:00 2001 From: Anthony Scopatz Date: Wed, 11 Feb 2015 02:01:35 -0600 Subject: [PATCH] seem to have working execer --- tests/test_execer.py | 18 +++++++++++++----- tests/test_parser.py | 19 ++++++------------- tests/tools.py | 20 ++++++++++++++++++++ xonsh/ast.py | 30 ++++++++++++++++++++++-------- xonsh/execer.py | 15 +++++++++++---- 5 files changed, 72 insertions(+), 30 deletions(-) create mode 100644 tests/tools.py diff --git a/tests/test_execer.py b/tests/test_execer.py index f19b49c71..b6164a9a9 100644 --- a/tests/test_execer.py +++ b/tests/test_execer.py @@ -6,6 +6,8 @@ import ast from xonsh.execer import Execer +from tools import mock_xonsh_env + DEBUG_LEVEL = 0 EXECER = None @@ -16,15 +18,21 @@ def setup(): def check_exec(input): - if not input.endswith('\n'): - input += '\n' - EXECER.debug_level = DEBUG_LEVEL - EXECER.exec(input) - + with mock_xonsh_env(None): + if not input.endswith('\n'): + input += '\n' + EXECER.debug_level = DEBUG_LEVEL + EXECER.exec(input) def test_bin_ls(): yield check_exec, '/bin/ls -l' +def test_ls_dashl(): + yield check_exec, 'ls -l' + +def test_which_ls(): + yield check_exec, 'which ls' + if __name__ == '__main__': nose.runmodule() diff --git a/tests/test_parser.py b/tests/test_parser.py index 893efd0f1..358840073 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -17,6 +17,8 @@ from ply.lex import LexToken from xonsh.parser import Parser +from tools import mock_xonsh_env + PARSER = None DEBUG_LEVEL = 0 #DEBUG_LEVEL = 100 @@ -68,19 +70,10 @@ def check_stmts(input, run=True): check_ast(input, run=run) def check_xonsh_ast(xenv, input, run=True): - builtins.__xonsh_env__ = xenv - builtins.__xonsh_help__ = lambda x: x - builtins.__xonsh_superhelp__ = lambda x: x - builtins.__xonsh_regexpath__ = lambda x: [] - builtins.__xonsh_subproc__ = subprocess - obs = PARSER.parse(input, debug_level=DEBUG_LEVEL) - if run: - exec(compile(obs, '', 'exec')) - del builtins.__xonsh_env__ - del builtins.__xonsh_help__ - del builtins.__xonsh_superhelp__ - del builtins.__xonsh_regexpath__ - del builtins.__xonsh_subproc__ + with mock_xonsh_env(xenv): + obs = PARSER.parse(input, debug_level=DEBUG_LEVEL) + if run: + exec(compile(obs, '', 'exec')) def check_xonsh(xenv, input, run=True): if not input.endswith('\n'): diff --git a/tests/tools.py b/tests/tools.py new file mode 100644 index 000000000..10556dda9 --- /dev/null +++ b/tests/tools.py @@ -0,0 +1,20 @@ +"""Tests the xonsh lexer.""" +from __future__ import unicode_literals, print_function +import builtins +import subprocess +from contextlib import contextmanager + +@contextmanager +def mock_xonsh_env(xenv): + builtins.__xonsh_env__ = xenv + builtins.__xonsh_help__ = lambda x: x + builtins.__xonsh_superhelp__ = lambda x: x + builtins.__xonsh_regexpath__ = lambda x: [] + builtins.__xonsh_subproc__ = subprocess + yield + del builtins.__xonsh_env__ + del builtins.__xonsh_help__ + del builtins.__xonsh_superhelp__ + del builtins.__xonsh_regexpath__ + del builtins.__xonsh_subproc__ + diff --git a/xonsh/ast.py b/xonsh/ast.py index 9747a639c..536ee9bce 100644 --- a/xonsh/ast.py +++ b/xonsh/ast.py @@ -9,7 +9,7 @@ from ast import Module, Num, Expr, Str, Bytes, UnaryOp, UAdd, USub, Invert, \ Del, Pass, Raise, Import, alias, ImportFrom, Continue, Break, Yield, \ YieldFrom, Return, IfExp, Lambda, arguments, arg, Call, keyword, \ Attribute, Global, Nonlocal, If, While, For, withitem, With, Try, \ - ExceptHandler, FunctionDef, ClassDef, Starred, NodeTransformer + ExceptHandler, FunctionDef, ClassDef, Starred, NodeTransformer, dump from xonsh.tools import subproc_line @@ -17,12 +17,14 @@ def leftmostname(node): """Attempts to find the first name in the tree.""" if isinstance(node, Name): rtn = node.id - elif isinstance(node, (Str, Bytes)): - rtn = node.s + #elif isinstance(node, (Str, Bytes)): + # rtn = node.s elif isinstance(node, (BinOp, Compare)): rtn = leftmostname(node.left) - elif isinstance(node, (Attribute, Subscript, Starred)): + elif isinstance(node, (Attribute, Subscript, Starred, Expr)): rtn = leftmostname(node.value) + elif isinstance(node, Call): + rtn = leftmostname(node.func) else: rtn = None return rtn @@ -89,12 +91,15 @@ class CtxAwareTransformer(NodeTransformer): break if inscope: return node - newline = subproc_line(self.lines[node.lineno]) + spline = subproc_line(self.lines[node.lineno - 1]) try: - node = self.parser.parse(newline) + newnode = self.parser.parse(spline) + newnode = newnode.body[0] # take the first (and only) Expr + newnode.lineno = node.lineno + newnode.col_offset = node.col_offset except SyntaxError as e: - pass - return node + newnode = node + return newnode def visit_Assign(self, node): for targ in node.targets: @@ -138,12 +143,16 @@ class CtxAwareTransformer(NodeTransformer): def visit_FunctionDef(self, node): self.ctxadd(node.name) + self.contexts.append(set()) self.generic_visit(node) + self.contexts.pop() return node def visit_ClassDef(self, node): self.ctxadd(node.name) + self.contexts.append(set()) self.generic_visit(node) + self.contexts.pop() return node def visit_Delete(self, node): @@ -159,3 +168,8 @@ class CtxAwareTransformer(NodeTransformer): self.ctxadd(handler.name) self.generic_visit(node) return node + + def visit_Global(self, node): + self.contexts[1].update(node.names) # contexts[1] is the global ctx + self.generic_visit(node) + return node diff --git a/xonsh/execer.py b/xonsh/execer.py index e858e8da2..6331d9e9f 100644 --- a/xonsh/execer.py +++ b/xonsh/execer.py @@ -2,6 +2,8 @@ from __future__ import print_function, unicode_literals import re import os +import inspect +import builtins from collections import Iterable, Sequence, Mapping from xonsh import ast @@ -36,6 +38,7 @@ class Execer(object): ctx = set() elif isinstance(ctx, Mapping): ctx = set(ctx.keys()) + # Parsing actually happens in a couple of phases. The first is a # shortcut for a context-free parser. Nomrally, all subprocess # lines should be wrapped in $(), to indicate that they are a @@ -61,11 +64,16 @@ class Execer(object): tree = self.ctxtransformer.ctxvisit(tree, input, ctx) return tree - def exec(self, input, globals=None, locals=None): + def exec(self, input, glbs=None, locs=None, stacklevel=1): """Execute xonsh code.""" - tree = self.parse(input, locals) + if glbs is None or locs is None: + frame = inspect.stack()[stacklevel][0] + glbs = frame.f_globals if glbs is None else glbs + locs = frame.f_locals if locs is None else locs + ctx = set(dir(builtins)) | set(glbs.keys()) | set(locs.keys()) + tree = self.parse(input, ctx) code = compile(tree, self.filename, 'exec') - #exec(code, globals, locals) + exec(code, glbs, locs) def _parse_ctx_free(self, input): last_error_line = -1 @@ -84,4 +92,3 @@ class Execer(object): lines[idx] = subproc_line(lines[idx]) input = '\n'.join(lines) return tree -