def RaisesRegexOp(context, designator, exceptionClass, expected_regex, indent, kws, arglist, node): expected_regex.prefix = "" arglist = [a.clone() for a in arglist.children] del arglist[2:4] # remove pattern and comma arglist = Node(syms.arglist, arglist) with_stmt = RaisesOp(context, exceptionClass, indent, kws, arglist, node) with_stmt.insert_child(2, Name('as', prefix=" ")) with_stmt.insert_child(3, Name(designator, prefix=" ")) # if this is already part of a with statement we need to insert re.search # after the last leaf with content if node.parent.type == syms.with_stmt: parent_with = node.parent for leaf in reversed(list(parent_with.leaves())): if leaf.value.strip(): break i = leaf.parent.children.index(leaf) leaf.parent.insert_child(i + 1, Newline()) leaf.parent.insert_child( i + 2, Name('assert %s.match(%s)' % (designator, expected_regex), prefix=indent)) return with_stmt else: return Node(syms.suite, [ with_stmt, Newline(), Name('assert %s.match(%s)' % (designator, expected_regex), prefix=indent) ])
def transform(self, node, results): signame = results['signame'] if 'emitter' in results: emitter = results.get("emitter") emitter = Name(''.join(map(str, emitter))) if 'sigarg' in results: args = results.get("args").clone() args.children = args.children[2:] if args.children: args.children[0].prefix = '' res = Node(syms.power, [emitter, Name('.'), Name(signame), Name('.'), Name('emit')] + [ArgList([args])]) else: res = Node(syms.power, [emitter, Name('.'), Name(signame), Name('.'), Name('emit()')]) else: sender = results.get("sender").clone() method = results.get("method") if isinstance(method, list): method = method[0] method = method.clone() sender.prefix = node.prefix slot = results.get("slot").clone() slot.prefix = "" res = Node(syms.power, [sender, Name('.'), Name(signame), Name('.'), method] + [ArgList([slot])]) return res
def transform(self, node, results): signal = results.get("signal").value signal = re.sub('^["\']([^(]+)(?:\(.*\))?["\']$', '\\1', signal) if 'emitter' in results: emitter = results.get("emitter").clone() emitter.prefix = node.prefix args = results.get("args").clone() args.children = args.children[2:] if args.children: args.children[0].prefix = '' res = Node( syms.power, [emitter, Name('.'), Name(signal), Name('.'), Name('emit')] + [ArgList([args])]) else: sender = results.get("sender").clone() method = results.get("method") if isinstance(method, list): method = method[0] method = method.clone() sender.prefix = node.prefix slot = results.get("slot").clone() slot.prefix = "" res = Node( syms.power, [sender, Name('.'), Name(signal), Name('.'), method] + [ArgList([slot])]) return res
def RaisesOp(context, exceptionClass, indent, kws, arglist): with_item = Call(Name(context), [exceptionClass]) with_item.prefix = " " args = [] arglist = [a.clone() for a in arglist.children[4:]] if arglist: arglist[0].prefix="" func = None # :fixme: this uses hardcoded parameter names, which may change if 'callableObj' in kws: func = kws['callableObj'] elif 'callable_obj' in kws: func = kws['callable_obj'] elif kws['args']: # any arguments assigned to `*args` func = kws['args'][0] else: raise NotImplementedError('with %s is not implemented' % context) if func is unittest.case._sentinel: # with self.assertRaises(SomeException): return Node(syms.with_stmt, [with_item]) suite = Call(func, arglist) suite.prefix = indent + (4 * " ") return Node(syms.with_stmt, [Name('with'), with_item, Name(':'), Newline(), suite])
def _new_type_check_with_import(package, name, root, insert_pos): # type: (Optional[str], str, Node, int) -> None """ Inserts a new TYPE_CHECKING block containing a new import statement for package and name Parameters ----------- package : Optional[str] name : str root : Node insert_pos : int """ # [Grammar] # if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite] type_check_node = Node(syms.if_stmt, [Leaf(token.NAME, 'if'), Leaf(token.NAME, 'TYPE_CHECKING', prefix=" "), Leaf(token.COLON, ':'), # [Grammar] # suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT Node(syms.suite, [Leaf(token.NEWLINE, '\n'), Leaf(token.INDENT, ' '), Node(syms.simple_stmt, [_generate_import_node(package, name), Newline()]), Leaf(token.DEDENT, '')])]) # We can just hardcode the correct insert position since we just created the typing block root.insert_child(insert_pos, type_check_node) # Make sure to import TYPE_CHECKING just before using import_type_checking = [_generate_import_node('typing', 'TYPE_CHECKING'), Newline()] root.insert_child(insert_pos, Node(syms.simple_stmt, import_type_checking))
def RaisesOp(context, exceptionClass, indent, kws, arglist, node): with_item = Call(Name(context), [exceptionClass]) with_item.prefix = " " args = [] arglist = [a.clone() for a in arglist.children[4:]] if arglist: arglist[0].prefix = "" func = None # :fixme: this uses hardcoded parameter names, which may change if 'callableObj' in kws: func = kws['callableObj'] elif 'callable_obj' in kws: func = kws['callable_obj'] elif kws['args']: # any arguments assigned to `*args` func = kws['args'][0] else: func = None if func is None: # Context manager return Node(syms.with_stmt, [with_item]) if func.type == syms.lambdef: suite = func.children[-1].clone() else: suite = Call(func, arglist) suite.prefix = indent + (4 * " ") return Node( syms.with_stmt, [Name('with'), with_item, Name(':'), Newline(), suite])
def _generate_import_node(package, name, prefix=""): def DottedName(name, prefix=""): split = name.rsplit('.') if len(split) > 1: # Reconstruct the dotted name as a list of leaves leftmost_name = Leaf(token.NAME, split[0]) children = [leftmost_name] for entry in split[1:]: next_name = [Leaf(token.DOT, '.'), Leaf(token.NAME, entry)] children.extend(next_name) return Node(syms.dotted_name, children, prefix=prefix) return Leaf(token.NAME, name, prefix=prefix) if not package: import_ = Node(syms.import_name, [ Leaf(token.NAME, "import", prefix=prefix), DottedName(name, prefix=" ") ]) else: import_ = Node(syms.import_from, [ Leaf(token.NAME, "from", prefix=prefix), DottedName(package, prefix=" "), Leaf(token.NAME, "import", prefix=" "), Leaf(token.NAME, name, prefix=" "), ]) return import_
def RaisesRegexOp(context, designator, exceptionClass, expected_regex, indent, kws, arglist): arglist = [a.clone() for a in arglist.children] del arglist[2:4] # remove pattern and comma arglist = Node(syms.arglist, arglist) with_stmt = RaisesOp(context, exceptionClass, indent, kws, arglist) with_stmt.insert_child(2, Name('as', prefix=" ")) with_stmt.insert_child(3, Name(designator, prefix=" ")) return Node(syms.suite, [with_stmt, Newline(), Name('assert re.search(pattern, %s.value)' % designator, prefix=indent) ])
def add_import(import_name, node): suite = get_parent_of_type(node, syms.suite) test_case = suite while test_case.parent.type != syms.file_input: test_case = test_case.parent file_input = test_case.parent if not does_tree_import(None, import_name, node): import_stmt = Node(syms.simple_stmt, [ Node( syms.import_name, [Name('import'), Name(import_name, prefix=' ')]), Newline(), ]) insert_import(import_stmt, test_case, file_input)
def handle_name(name, prefix): if name.type == syms.import_as_name: kids = [Name(name.children[0].value, prefix=prefix), name.children[1].clone(), name.children[2].clone()] return [Node(syms.import_as_name, kids)] return [Name(name.value, prefix=prefix)]
def future_import2(feature, node): """ An alternative to future_import() which might not work ... """ root = find_root(node) if does_tree_import(u"__future__", feature, node): return insert_pos = 0 for idx, node in enumerate(root.children): if node.type == syms.simple_stmt and node.children and \ node.children[0].type == token.STRING: insert_pos = idx + 1 break for thing_after in root.children[insert_pos:]: if thing_after.type == token.NEWLINE: insert_pos += 1 continue prefix = thing_after.prefix thing_after.prefix = u"" break else: prefix = u"" import_ = FromImport(u"__future__", [Leaf(token.NAME, feature, prefix=u" ")]) children = [import_, Newline()] root.insert_child(insert_pos, Node(syms.simple_stmt, children, prefix=prefix))
def match(self, node): u""" Since the tree needs to be fixed once and only once if and only if it matches, we can start discarding matches after the first. """ if node.type == self.syms.term: matched = False skip = False children = [] for child in node.children: if skip: skip = False continue if match_division(child) and not is_floaty(child): matched = True # Strip any leading space for the first number: children[0].prefix = u'' children = [ wrap_in_fn_call( "old_div", children + [Comma(), child.next_sibling.clone()], prefix=node.prefix) ] skip = True else: children.append(child.clone()) if matched: return Node(node.type, children, fixers_applied=node.fixers_applied) return False
def transform(self, node: LN, capture: Capture) -> None: full_name = [] trailer: List[LN] = [] for i, n in enumerate(node.children): if n.type == token.NAME: full_name.append(n.value) elif n.type == syms.trailer: if n.children[0].type != token.DOT: trailer = node.children[i:] break full_name.append(n.children[1].value) else: trailer = node.children[i:] try: new_name = self.renames.get_new_name(".".join(full_name)) except NameRemovedError as exc: self.warn(node, str(exc)) return if new_name: new_node = Node(syms.power, AttrChain(new_name), prefix=node.prefix) for n in trailer: new_node.append_child(n) node.replace(new_node)
def Import(name_leafs): for leaf in name_leafs: # Pull the leaves out of their old tree leaf.remove() def add_commas(leafs): yield leafs[0] for a in leafs[1:]: yield Comma() yield a children = [Leaf(token.NAME, u'import'), Node(syms.dotted_as_names, list(add_commas(name_leafs)))] imp = Node(syms.import_name, children) return imp
def transform(self, node, results): FIXME name, val, trc = ( results.get(u"name"), results.get(u"val"), results.get(u"trc"), ) chain = results.get(u"chain") if chain is not None: self.warning( node, u"explicit exception chaining is not supported in Python 2") chain.prev_sibling.remove() chain.remove() if trc is not None: val = val[0] if val else Leaf(token.NAME, u"None") val.prefix = trc.prefix = u" " kids = [ Leaf(token.NAME, u"raise"), name.clone(), Comma(), val.clone(), Comma(), trc.clone(), ] raise_stmt = Node(syms.raise_stmt, kids) node.replace(raise_stmt)
def transform(self, node, results): meta_results = has_metaclass(node) if not meta_results: return for meta in meta_results: meta.remove() target = Leaf(token.NAME, u"__metaclass__") equal = Leaf(token.EQUAL, u"=", prefix=u" ") # meta is the last item in what was returned by has_metaclass(): name name = meta name.prefix = u" " stmt_node = Node(syms.atom, [target, equal, name]) suitify(node) for item in node.children: if item.type == syms.suite: for stmt in item.children: if stmt.type == token.INDENT: # Insert, in reverse order, the statement, a newline, # and an indent right after the first indented line loc = item.children.index(stmt) + 1 # Keep consistent indentation form ident = Leaf(token.INDENT, stmt.value) item.insert_child(loc, ident) item.insert_child(loc, Newline()) item.insert_child(loc, stmt_node) break
def RaisesOp(context, exceptionClass, indent, kws, arglist, node): exceptionClass.prefix = "" args = [exceptionClass] # Add match keyword arg to with statement if an expected regex was provided. # In py27 the keyword is `expected_regexp`, in py3 is `expected_regex` if 'expected_regex' in kws or 'expected_regexp' in kws: expected_regex = kws.get('expected_regex', kws.get('expected_regexp')).clone() expected_regex.prefix = '' args.append(String(', ')) args.append(KeywordArg(Name('match'), expected_regex)) with_item = Call(Name(context), args) with_item.prefix = " " args = [] arglist = [a.clone() for a in arglist.children[4:]] if arglist: arglist[0].prefix = "" func = None # :fixme: this uses hardcoded parameter names, which may change if 'callableObj' in kws: func = kws['callableObj'] elif 'callable_obj' in kws: func = kws['callable_obj'] elif kws['args']: # any arguments assigned to `*args` func = kws['args'][0] else: func = None if func is None: # Context manager return Node(syms.with_stmt, [with_item]) if func.type == syms.lambdef: suite = func.children[-1].clone() else: # TODO: Newlines within arguments are not handled yet. # If argment prefix contains a newline, all whitespace around this # ought to be replaced by indent plus 4+1+len(func) spaces. suite = Call(func, arglist) suite.prefix = indent + (4 * " ") return Node( syms.with_stmt, [Name('with'), with_item, Name(':'), Newline(), suite])
def ImportAsName(name, as_name, prefix=None): new_name = Name(name) new_as = Name(u"as", prefix=u" ") new_as_name = Name(as_name, prefix=u" ") new_node = Node(syms.import_as_name, [new_name, new_as, new_as_name]) if prefix is not None: new_node.prefix = prefix return new_node
def ImportAsName(name: str, nick: Optional[str], prefix: Optional[str] = None) -> Node: if not nick: return Name(name, prefix=prefix) return Node( syms.import_as_name, [Name(name), Name("as", prefix=" "), Name(nick, prefix=" ")], prefix=prefix, )
def CompOp(op, left, right, kws): op = Name(op, prefix=" ") left = parenthesize_expression(left) right = parenthesize_expression(right) left.prefix = "" if '\n' not in right.prefix: right.prefix = " " return Node(syms.comparison, (left, op, right), prefix=" ")
def transform(self, node, result): if len(node.children) == 3 and node.children[-1].type == token.NUMBER: userdata_id = int(node.children[-1].value) replacement = self.subfun(userdata_id) if replacement: new = Node(python_symbols.subscriptlist, [Leaf(token.NAME, replacement)]) return new return None
def UnaryOp(prefix, postfix, value, kws): kids = [] if prefix: kids.append(Name(prefix, prefix=" ")) value.prefix = " " kids.append(value) if postfix: kids.append(Name(postfix, prefix=" ")) return Node(syms.test, kids, prefix=" ")
def RaisesRegexOp(context, designator, exceptionClass, expected_regex, indent, kws, arglist, node): arglist = [a.clone() for a in arglist.children] pattern = arglist[2] del arglist[2:4] # remove pattern and comma arglist = Node(syms.arglist, arglist) with_stmt = RaisesOp(context, exceptionClass, indent, kws, arglist, node) # if this is already part of a with statement we need to insert re.search # after the last leaf with content if node.parent.type == syms.with_stmt: parent_with = node.parent for leaf in reversed(list(parent_with.leaves())): if leaf.value.strip(): break i = leaf.parent.children.index(leaf) return with_stmt else: return Node(syms.suite, [with_stmt])
def DottedName(full_name: str, prefix: Optional[str]) -> Node: names = [name.strip() for name in full_name.split(".")] dotted_name = [] for name in names: dotted_name.append(Name(name)) dotted_name.append(Dot()) dotted_name.pop() return Node(syms.dotted_name, dotted_name, prefix=prefix)
def FromImport( package: str, imports: List[Tuple[str, Optional[str]]], prefix: Optional[str] ) -> Node: children = [] for name, nick in imports: children.append(ImportAsName(name, nick, prefix=" ")) children.append(Comma()) children.pop() import_as_names = Node(syms.import_as_names, children) return Node( syms.import_from, [ Name("from", prefix=prefix), DottedName(package, prefix=" "), Name("import", prefix=" "), import_as_names, ], )
def DottedName(name, prefix=""): split = name.rsplit('.') if len(split) > 1: # Reconstruct the dotted name as a list of leaves leftmost_name = Leaf(token.NAME, split[0]) children = [leftmost_name] for entry in split[1:]: next_name = [Leaf(token.DOT, '.'), Leaf(token.NAME, entry)] children.extend(next_name) return Node(syms.dotted_name, children, prefix=prefix) return Leaf(token.NAME, name, prefix=prefix)
def transform(self, node, results): def process_arg(arg): if isinstance(arg, Leaf) and arg.type == token.COMMA: return elif isinstance(arg, Node) and arg.type == syms.argument: # keyword argument name, equal, value = arg.children assert name.type == token.NAME # what is the symbol for 1? assert equal.type == token.EQUAL # what is the symbol for 1? value = value.clone() value.prefix = " " kwargs[name.value] = value else: assert not kwargs, 'all positional args are assumed to come first' posargs.append(arg.clone()) method = results['method'][0].value # map (deprecated) aliases to original to avoid analysing # the decorator function method = _method_aliases.get(method, method) posargs = [] kwargs = {} # This is either a "arglist" or a single argument if results['arglist'].type == syms.arglist: for arg in results['arglist'].children: process_arg(arg) else: process_arg(results['arglist']) try: test_func = getattr(unittest.TestCase, method) except AttributeError: raise RuntimeError("Your unittest package does not support '%s'. " "consider updating the package" % method) required_args, argsdict = utils.resolve_func_args(test_func, posargs, kwargs) if method.startswith(('assertRaises', 'assertWarns')): n_stmt = _method_map[method](*required_args, indent=find_indentation(node), kws=argsdict, arglist=results['arglist']) else: n_stmt = Node(syms.assert_stmt, [Name('assert'), _method_map[method](*required_args, kws=argsdict)]) if argsdict.get('msg', None) is not None: n_stmt.children.extend((Name(','), argsdict['msg'])) n_stmt.prefix = node.prefix return n_stmt
def UnaryOp(prefix, postfix, value, kws): if prefix or postfix: value = parenthesize_expression(value) kids = [] if prefix: kids.append(Name(prefix, prefix=" ")) value.prefix = " " kids.append(value) if postfix: kids.append(Name(postfix, prefix=" ")) return Node(syms.test, kids, prefix=" ")
def match(self, node): u""" Since the tree needs to be fixed once and only once if and only if it matches, we can start discarding matches after the first. """ if node.type == self.syms.term: matched = False skip = False children = [] for child in node.children: if skip: skip = False continue if match_division(child) and not is_floaty(child): matched = True # Strip any leading space for the first number: children[0].prefix = u'' children = [ wrap_in_fn_call( "old_div", children + [Comma(), child.next_sibling.clone()], prefix=node.prefix) ] skip = True else: children.append(child.clone()) if matched: # In Python 2.6, `Node` does not have the fixers_applied attribute # https://github.com/python/cpython/blob/8493c0cd66cfc181ac1517268a74f077e9998701/Lib/lib2to3/pytree.py#L235 if hasattr(Node, "fixers_applied"): return Node(node.type, children, fixers_applied=node.fixers_applied) else: return Node(node.type, children) return False
def fixup_simple_stmt(parent, i, stmt_node): """ if there is a semi-colon all the parts count as part of the same simple_stmt. We just want the __metaclass__ part so we move everything efter the semi-colon into its own simple_stmt node """ for semi_ind, node in enumerate(stmt_node.children): if node.type == token.SEMI: # *sigh* break else: return node.remove() # kill the semicolon new_expr = Node(syms.expr_stmt, []) new_stmt = Node(syms.simple_stmt, [new_expr]) while stmt_node.children[semi_ind:]: move_node = stmt_node.children[semi_ind] new_expr.append_child(move_node.clone()) move_node.remove() parent.insert_child(i, new_stmt) new_leaf1 = new_stmt.children[0].children[0] old_leaf1 = stmt_node.children[0].children[0] new_leaf1.prefix = old_leaf1.prefix