not working yet

This commit is contained in:
Anthony Scopatz 2016-06-05 14:32:59 -04:00
parent fe5fc96dc9
commit d1ec37f914
5 changed files with 107 additions and 11 deletions

20
tests/test_ast.py Normal file
View file

@ -0,0 +1,20 @@
"""Xonsh AST tests."""
from nose.tools import assert_equal
from xonsh import ast
from xonsh.ast import Tuple, Name, Store
def test_gather_names_name():
node = Name(id='y', ctx=Store())
exp = {'y'}
obs = ast.gather_names(node)
assert_equal(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_equal(exp, obs)

View file

@ -60,6 +60,22 @@ def leftmostname(node):
return rtn
def get_lineno(node, default=0):
"""Gets the lineno of a node or returns the default."""
return getattr(node, 'lineno', default)
def min_line(node):
"""Computes the minimum lineno."""
node_line = get_lineno(node)
return min(map(get_lineno, walk(node), repeat(node_line)))
def max_line(node):
"""Computes the maximum lineno."""
return max(map(get_lineno, walk(node)))
def get_col(node, default=-1):
"""Gets the col_offset of a node, or returns the default"""
return getattr(node, 'col_offset', default)
@ -78,6 +94,23 @@ def max_col(node):
return col
def get_id(node, default=None):
"""Gets the id attribute of a node, or returns a default."""
return getattr(node, 'id', default)
def gather_names(node):
"""Returns the set of all names present in the node's tree."""
rtn = set(map(get_id, walk(node)))
rtn.discard(None)
return rtn
def has_elts(x):
"""Tests if x is an AST node with elements."""
return isinstance(x, AST) and hasattr(x, 'elts')
def isdescendable(node):
"""Deteremines whether or not a node is worth visiting. Currently only
UnaryOp and BoolOp nodes are visited.
@ -104,6 +137,7 @@ class CtxAwareTransformer(NodeTransformer):
self.contexts = []
self.lines = None
self.mode = None
self._nwith = 0
def ctxvisit(self, node, inp, ctx, mode='exec'):
"""Transforms the node in a context-dependent way.
@ -125,8 +159,10 @@ class CtxAwareTransformer(NodeTransformer):
self.lines = inp.splitlines()
self.contexts = [ctx, set()]
self.mode = mode
self._nwith = 0
node = self.visit(node)
del self.lines, self.contexts, self.mode
self._nwith = 0
return node
def ctxupdate(self, iterable):
@ -191,6 +227,43 @@ class CtxAwareTransformer(NodeTransformer):
break
return inscope
def insert_with_block_check(self, node):
"""Modifies a with statement node in-place to add an initial check
for whether or not the block should be executed. If the block is
not executed it will raise a XonshBlockError containing the required
information.
"""
nwith = self._nwith # the nesting level of the current with-statement
lineno = get_lineno(node)
col = get_col(node, 0)
# Add or discover target names
targets = set()
i = 0 # index of unassigned items
def make_next_target():
targ = '__xonsh_with_target_{}_{}__'.format(nwith, i)
node = Name(id=targ, ctx=Store(), lineno=lineno, col_offset=col)
targets.add(targ)
i += 1
return node
for item in node.items:
if item.optional_vars is None:
if has_elts(item.context_expr):
targs = [make_next_target() for _ in item.context_expr.elts]
optvars = Tuple(elts=targs, ctx=Store(), lineno=lineno,
col_offset=col)
else:
optvars = make_next_target()
item.optional_vars = optvars
else:
targets.update(gather_names(item.optional_vars))
# Ok, now that targets have been found / created, make the actual check
# to see if we are in a non-executing block. This is equivalent to writing
#
# if getattr(targ0, '__xonsh_block__', False) or \
# getattr(targ1, '__xonsh_block__', False) or ...:
# raise XonshBlockError("<lines>", globals(), locals())
#
# Replacement visitors
#
@ -286,8 +359,11 @@ class CtxAwareTransformer(NodeTransformer):
"""Handle visiting a with statement."""
for item in node.items:
if item.optional_vars is not None:
self.ctxadd(leftmostname(item.optional_vars))
self.ctxupdate(gather_names(item.optional_vars))
self._nwith += 1
self.generic_visit(node)
self._nwith -= 1
#self.insert_with_block_check(node)
return node
def visit_For(self, node):
@ -365,4 +441,6 @@ def pdump(s, **kwargs):
return pre + mid + post
def pprint(s, *, sep=None, end=None, file=None, flush=False, **kwargs):
"""Performs a pretty print of the AST nodes."""
print(pdump(s, **kwargs), sep=sep, end=end, file=file, flush=flush)

View file

@ -31,5 +31,7 @@ class Block(object):
return # some other kind of error happened
self.lines = exc_value.lines
self.glbs = exc_value.glbs
self.locs = exc_value.locs
if exc_value.locs is not self.glbs:
# leave locals as None when it is the same as globals
self.locs = exc_value.locs
return True

View file

@ -8,6 +8,7 @@ except ImportError:
from xonsh.ply import yacc
from xonsh import ast
from xonsh.ast import has_elts
from xonsh.lexer import Lexer, LexToken
from xonsh.platform import PYTHON_VERSION_INFO
@ -28,11 +29,6 @@ class Location(object):
return s
def has_elts(x):
"""Tests if x is an AST node with elements."""
return isinstance(x, ast.AST) and hasattr(x, 'elts')
def ensure_has_elts(x, lineno=None, col_offset=None):
"""Ensures that x is an AST node with elements."""
if not has_elts(x):

View file

@ -48,15 +48,15 @@ class XonshError(Exception):
class XonshBlockError(XonshError):
"""Special xonsh exception for communicating the liens of block bodies."""
def __init__(self, lines, glbs=None, locs=None, *args, **kwargs):
def __init__(self, lines, glbs, locs, *args, **kwargs):
"""
Parameters
----------
lines : str
Block lines.
glbs : Mapping or None, optional
glbs : Mapping or None
Global execution context for lines, ie globals() of calling frame.
locs : Mapping or None, optional
locs : Mapping or None
Local execution context for lines, ie locals() of calling frame.
"""
super().__init__(*args, **kwargs)