def from_node(cls, node): checktype(node, L.Aggregate) if isinstance(node.value, L.DemQuery): assert all(isinstance(a, L.Name) for a in node.value.args) oper_demparams = tuple(a.id for a in node.value.args) oper_demname = node.value.demname oper = node.value.value else: oper_demparams = None oper_demname = None oper = node.value if isinstance(oper, L.Name): rel = oper.id relmask = Mask.U params = () elif (isinstance(oper, L.SetMatch) and isinstance(oper.target, L.Name) and L.is_vartuple(oper.key)): rel = oper.target.id relmask = Mask(oper.mask) params = L.get_vartuple(oper.key) else: raise L.ProgramError('Bad aggregate operand', node=node) return cls(node.op, rel, relmask, params, oper_demname, oper_demparams)
def get_mapassign(node): """Match an Assign node of form <alpha>[<beta>] = <value> and returns alpha, beta, and value. As a special case, if alpha is the function call "globals()", do not match. """ checktype(node, AST) if (isinstance(node, Assign) and len(node.targets) == 1 and isinstance(node.targets[0], Subscript) and isinstance(node.targets[0].slice, Index)): # Catch globals(). target = node.targets[0].value if (isinstance(target, Call) and isinstance(target.func, Name) and target.func.id == 'globals'): pass else: return (node.targets[0].value, node.targets[0].slice.value, node.value) from . import ts raise TypeError('get_mapassign failed: ' + ts(node))
def __new__(cls, parts): """Construct from a string, or a sequence of strings.""" # Validate parts. try: checktype(parts, str) except TypeError: checktype_seq(parts, str) parts = tuple(parts) if not all(c == 'b' or c == 'u' or (c.isdigit() and c != '0') or c == 'w' for c in parts): raise ValueError('Invalid pattern mask: ' + ''.join(parts)) if any(c.isdigit() and len(c) > 1 for c in parts): raise ValueError('Equality constraints with index > 9 ' 'not supported') if any(c.isdigit() and int(c) - 1 >= i for i, c in enumerate(parts)): raise ValueError('Equality constraint must refer to smaller ' 'index than own occurrence') return super().__new__(cls, parts)
def is_plainfuncdef(func): """Returns True if the function has no fancy arguments. (Note that func is a FunctionDef statement, not code. """ checktype(func, FunctionDef) plain_args = tuple(arg(a.arg, None) for a in func.args.args) plain_arguments = arguments(plain_args, None, (), (), None, ()) return func.args == plain_arguments
def extract_tree(L, tree, mode=None): """Given a tree rooted at a Module node, return a subtree as selected by mode, which is one of the following strings. mod: Return the original tree, unchanged. (default) code: Get the list of top-level statements. stmt_or_blank: The one top-level statement, or None if there are no statements. stmt: The one top-level statement. expr: The one top-level expression. lval: The one top-level expression, in Store context. """ checktype(tree, L.Module) if mode == 'mod' or mode is None: pass elif mode == 'code': tree = tree.body elif mode == 'stmt_or_blank': if len(tree.body) == 0: return None elif len(tree.body) == 1: tree = tree.body[0] else: raise ValueError('Mode "{}" requires zero or one statements ' '(got {})'.format(mode, len(tree.body))) elif mode in ['stmt', 'expr', 'lval']: if len(tree.body) != 1: raise ValueError('Mode "{}" requires exactly one statement ' '(got {})'.format(mode, len(tree.body))) tree = tree.body[0] if mode in ['expr', 'lval']: if not isinstance(tree, L.Expr): raise ValueError('Mode "{}" requires Expr node (got {})' .format(mode, type(tree).__name__)) tree = tree.value if mode == 'lval': tree = ContextSetter.run(tree, L.Store) elif mode is not None: raise ValueError('Unknown parse mode "' + mode + '"') return tree
def prefix_names(tree, names, prefix): """Given a tree and a sequence of names, produce a new tree where each occurrence of those names is prefixed with the given string. """ checktype_seq(names, str) checktype(prefix, str) subst = {n: prefix + n for n in names} return Templater.run(tree, subst)
def get_name(node): """Match a Name node and return the identifier.""" checktype(node, AST) if isinstance(node, Name): return node.id from . import ts raise TypeError('get_name failed: ' + ts(node))
def get_namematch(node): """Match a SetMatch over a Name, and return a triple of the name, mask, and key. """ checktype(node, SetMatch) if isinstance(node.target, Name): return (node.target.id, node.mask, node.key) from . import ts raise TypeError('get_namematch failed: ' + ts(node))
def get_namesmlookup(node): """Match a SMLookup node over a name, and return a triple of the name, mask, and key. """ checktype(node, SMLookup) if isinstance(node.target, Name): return (node.target.id, node.mask, node.key) from . import ts raise TypeError('get_namesmlookup failed: ' + ts(node))
def get_cmp(node): """Match a Compare node of two operands, and return a triple of the first operand, the operation, and the second operand. """ checktype(node, AST) if (isinstance(node, Compare) and len(node.ops) == len(node.comparators) == 1): return node.left, node.ops[0], node.comparators[0] from . import ts raise TypeError('get_cmp failed: ' + ts(node))
def get_plaincall(node): """Match a call of a Name node with only positional arguments. Return the function name and a tuple of the argument expressions. """ checktype(node, AST) if (isinstance(node, Call) and isinstance(node.func, Name) and node.keywords == () and node.starargs is None and node.kwargs is None): return node.func.id, node.args from . import ts raise TypeError('get_importstar failed: ' + ts(node))
def get_vartuple(node): """Match a Name or Tuple of Names and return a tuple of the identifiers. """ checktype(node, AST) if isinstance(node, Name): return (node.id, ) elif (isinstance(node, Tuple) and all(isinstance(item, Name) for item in node.elts)): return tuple(item.id for item in node.elts) from . import ts raise TypeError('get_vartuple failed: ' + ts(node))
def get_singletonset(node): """Match a singleton set, i.e. {val} and return val. """ checktype(node, AST) if isinstance(node, Set) and len(node.elts) == 1: return node.elts[0] from . import ts raise TypeError('get_singletonset failed: ' + ts(node))
def get_vartuple(node): """Match a Name or Tuple of Names and return a tuple of the identifiers. """ checktype(node, AST) if isinstance(node, Name): return (node.id,) elif (isinstance(node, Tuple) and all(isinstance(item, Name) for item in node.elts)): return tuple(item.id for item in node.elts) from . import ts raise TypeError('get_vartuple failed: ' + ts(node))
def f(*args, **kargs): ba = sig.bind(*args, **kargs) for name, val in ba.arguments.items(): ann = sig.parameters[name].annotation if ann is Parameter.empty: pass elif ann == 'Str': checktype(val, L.Str) ba.arguments[name] = val.s elif ann == 'Num': checktype(val, L.Num) ba.arguments[name] = val.n elif ann == 'Name': checktype(val, L.Name) ba.arguments[name] = val.id elif ann == 'List': checktype(val, L.List) ba.arguments[name] = val.elts elif ann == 'ids': if not isinstance(val, (L.List, L.Tuple)): raise TypeError('Expected List or Tuple node') checktype_seq(val.elts, L.Name) ba.arguments[name] = tuple(v.id for v in val.elts) else: raise TypeError('Unknown astarg specifier "{}"'.format(ann)) return func(*ba.args, **ba.kwargs)
def from_expr(cls, node): """Construct from a membership expression (<var>, <var>) in _M """ checktype(node, L.AST) left, op, right = L.get_cmp(node) checktype(op, L.In) lhs = L.get_vartuple(left) assert len(lhs) == 2 cont, item = lhs rel = L.get_name(right) assert is_mrel(rel) return cls(cont, item)
def get_varassign(node): """Match an Assign node of form <Name> = <value> and return Name.id and value. """ checktype(node, AST) if (isinstance(node, Assign) and len(node.targets) == 1 and isinstance(node.targets[0], Name)): return node.targets[0].id, node.value from . import ts raise TypeError('get_varassign failed: ' + ts(node))
def get_singsub(node): """Match a singleton set subtracted from an expression, i.e. <expr1> - {<expr2>} and return the two expressions. """ checktype(node, AST) if (isinstance(node, BinOp) and isinstance(node.op, Sub) and isinstance(node.right, Set) and len(node.right.elts) == 1): return node.left, node.right.elts[0] from . import ts raise TypeError('get_singsub failed: ' + ts(node))
def get_attrassign(node): """Match an Assign node of form <alpha>.<attr> = <value> and returns alpha, attr, and value. """ checktype(node, AST) if (isinstance(node, Assign) and len(node.targets) == 1 and isinstance(node.targets[0], Attribute)): return node.targets[0].value, node.targets[0].attr, node.value from . import ts raise TypeError('get_attrassign failed: ' + ts(node))
def from_AST(cls, node, factory): """Construct from Enumerator node of form (<var>, <var>, <var>) in _MAP """ checktype(node, L.Enumerator) lhs = L.get_vartuple(node.target) rel = L.get_name(node.iter) if not len(lhs) == 3: raise TypeError map, key, value = lhs if not is_maprel(rel): raise TypeError return cls(map, key, value)
def get_delattr(node): """Match a Delete node of form del <alpha>.<attr> and return alpha and attr. """ checktype(node, AST) if (isinstance(node, Delete) and len(node.targets) == 1 and isinstance(node.targets[0], Attribute)): return node.targets[0].value, node.targets[0].attr from . import ts raise TypeError('get_delattr failed: ' + ts(node))
def from_AST(cls, node, factory): """Construct from Enumerator node of form (<var>, <var>) in _M """ checktype(node, L.Enumerator) lhs = L.get_vartuple(node.target) rel = L.get_name(node.iter) if not len(lhs) == 2: raise TypeError cont, item = lhs if not is_mrel(rel): raise TypeError return cls(cont, item)
def get_importstar(node): """Match an import statement of form from <mod> import * and return mod. """ checktype(node, AST) if (isinstance(node, ImportFrom) and len(node.names) == 1 and node.names[0].name == '*' and node.level == 0): return node.module from . import ts raise TypeError('get_importstar failed: ' + ts(node))
def from_AST(cls, node, factory): """Construct from enumerator of form (tupvar, elt1, ..., eltn) in _TUPN """ checktype(node, L.Enumerator) lhs = L.get_vartuple(node.target) rel = L.get_name(node.iter) if not is_trel(rel): raise TypeError tup, *elts = lhs arity = get_trel(rel) assert arity == len(elts) return cls(tup, tuple(elts))
def get_vareqcmp(node): """Match a Compare node of form <Name 1> == <Name 2> == ... == <Name n> and return a tuple of the identifiers. """ checktype(node, AST) if (isinstance(node, Compare) and all(isinstance(op, Eq) for op in node.ops) and isinstance(node.left, Name) and all(isinstance(c, Name) for c in node.comparators)): return (node.left.id, ) + tuple(c.id for c in node.comparators) from . import ts raise TypeError('get_vareqcmp failed: ' + ts(node))
def get_vareqcmp(node): """Match a Compare node of form <Name 1> == <Name 2> == ... == <Name n> and return a tuple of the identifiers. """ checktype(node, AST) if (isinstance(node, Compare) and all(isinstance(op, Eq) for op in node.ops) and isinstance(node.left, Name) and all(isinstance(c, Name) for c in node.comparators)): return (node.left.id,) + tuple(c.id for c in node.comparators) from . import ts raise TypeError('get_vareqcmp failed: ' + ts(node))
def from_AST(cls, node, factory): """Construct from Enumerator node of form <vars> in DemQuery(...) """ checktype(node, L.Enumerator) if not isinstance(node.iter, L.DemQuery): raise TypeError if not all(isinstance(a, L.Name) for a in node.iter.args): raise TypeError demname = node.iter.demname demparams = tuple(a.id for a in node.iter.args) rhs = node.iter.value innernode = node._replace(iter=rhs) innerclause = factory.from_AST(innernode) return cls(innerclause, demname, demparams)
def cmp(left, op, right): """Comparison with only two operands.""" checktype(left, AST) checktype(op, AST) checktype(right, AST) return Compare(left, (op, ), (right, ))
def cmp(left, op, right): """Comparison with only two operands.""" checktype(left, AST) checktype(op, AST) checktype(right, AST) return Compare(left, (op,), (right,))
def get_delmap(node): """Match a Delete node of form del <alpha>[<beta>] and return alpha and beta. As a special case, if alphas is the function call "globals()", do not match. """ checktype(node, AST) if (isinstance(node, Delete) and len(node.targets) == 1 and isinstance(node.targets[0], Subscript) and isinstance(node.targets[0].slice, Index)): target = node.targets[0].value if (isinstance(target, Call) and isinstance(target.func, Name) and target.func.id == 'globals'): pass else: return node.targets[0].value, node.targets[0].slice.value from . import ts raise TypeError('get_delmap failed: ' + ts(node))
def get_setunion(node): """Match a union of set literals, set comprehensions, and names, and return a tuple of the individual set expression ASTs. """ checktype(node, AST) class Flattener(NodeVisitor): # BinOp nodes are tree-structured. Flatten the tree. class Failure(BaseException): pass def process(self, tree): self.parts = [] super().process(tree) return tuple(self.parts) def visit_BinOp(self, node): if not isinstance(node.op, BitOr): raise self.Failure self.visit(node.left) self.visit(node.right) def generic_visit(self, node): # Don't recurse. Just add this node as an operand. self.parts.append(node) try: parts = Flattener.run(node) if all(isinstance(p, (Set, Comp, Name)) for p in parts): return parts except Flattener.Failure: pass from . import ts raise TypeError('get_setunion failed: ' + ts(node))
def __new__(cls, parts): """Construct from a string, or a sequence of strings.""" # Validate parts. try: checktype(parts, str) except TypeError: checktype_seq(parts, str) parts = tuple(parts) if not all( c == 'b' or c == 'u' or (c.isdigit() and c != '0') or c == 'w' for c in parts): raise ValueError('Invalid pattern mask: ' + ''.join(parts)) if any(c.isdigit() and len(c) > 1 for c in parts): raise ValueError('Equality constraints with index > 9 ' 'not supported') if any(c.isdigit() and int(c) - 1 >= i for i, c in enumerate(parts)): raise ValueError('Equality constraint must refer to smaller ' 'index than own occurrence') return super().__new__(cls, parts)
def rewrite_compclauses(comp, rewriter, *, after=False, enum_only=False, recursive=False, resexp=True): """Apply a rewriter to each part of the comprehension, sometimes inserting new clauses to the left or right of the rewritten part. rewriter is applied to each clause in left-to-right order, and finally to the result expression. It returns a pair of the new clause/expression AST, and a list of clauses to be inserted immediately before or after this AST (depending on the after flag). If clauses are generated for the result expression, they are always appended to the end of the clause list, regardless of the after flag. If enum_only is True, only process enumerator clauses, not condition clauses or the result expression. If enum_only is False but resexp is True, process all clauses but not the result expression. If recursive is True, also call rewriter on the newly inserted clauses as well. """ checktype(comp, Comp) # Use a recursive function to handle the case of recursively # processing added clauses. def process(clauses): result = [] for cl in clauses: if enum_only and not isinstance(cl, Enumerator): result.append(cl) continue mod_clause, add_clauses = rewriter(cl) if recursive: add_clauses = process(add_clauses) if after: result.append(mod_clause) result.extend(add_clauses) else: result.extend(add_clauses) result.append(mod_clause) return result new_clauses = process(comp.clauses) # Handle result expression. if enum_only or not resexp: new_resexp = comp.resexp else: new_resexp, add_clauses = rewriter(comp.resexp) if recursive: add_clauses = process(add_clauses) new_clauses.extend(add_clauses) return comp._replace(resexp=new_resexp, clauses=tuple(new_clauses))
def ln(id): checktype(id, str) return Name(id, Load())
def sn(id): checktype(id, str) return Name(id, Store())
def dn(id): checktype(id, str) return Name(id, Del())