def transform(self, node, results): # before before = [b.clone() for b in results['before']] # tr | translate tr = results['tr'] new_tr = [(tr[0] if isinstance(tr, list) else tr).clone()] # message message = results['message'].clone() for ch in (message.pre_order() if not isinstance(message, Leaf) else [message]): if isinstance(ch, Leaf): for i in range(N_ARGS): # %1 -> {0}, ... ch.value = ch.value.replace("%{0}".format(i+1), "{%d}" % i) new_tr += [ArgList([message])] # format def format_args(): for key in sorted(results): if key.startswith('arg'): arg = results[key] if isinstance(arg, list): for a in arg: yield a.clone() else: yield arg.clone() yield Comma() yield String(' ') # Skip last Comma, String new_format = [Dot(), Name('format'), ArgList(list(format_args())[:-2])] # rest rest = [r.clone() for r in results['rest']] # new node new = Node(syms.power, before + new_tr + new_format + rest) new.prefix = node.prefix new.parent = node.parent return new
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 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 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 and arg.children[1].type == token.EQUAL): # keyword argument name, equal, value = arg.children assert name.type == token.NAME assert equal.type == token.EQUAL value = value.clone() kwargs[name.value] = value if '\n' in arg.prefix: value.prefix = arg.prefix else: value.prefix = arg.prefix.strip() + " " else: if (isinstance(arg, Node) and arg.type == syms.argument and arg.children[0].type == 36 and arg.children[0].value == '**'): return assert not kwargs, 'all positional args are assumed to come first' if (isinstance(arg, Node) and arg.type == syms.argument and arg.children[1].type == syms.comp_for): # argument is a generator expression w/o # parenthesis, add parenthesis value = arg.clone() value.children.insert(0, Leaf(token.LPAR, '(')) value.children.append(Leaf(token.RPAR, ')')) posargs.append(value) else: 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'], node=node) 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'])) def fix_line_wrapping(x): for c in x.children: # no need to worry about wrapping of "[", "{" and "(" if c.type in [token.LSQB, token.LBRACE, token.LPAR]: break if c.prefix.startswith('\n'): c.prefix = c.prefix.replace('\n', ' \\\n') fix_line_wrapping(c) fix_line_wrapping(n_stmt) # the prefix should be set only after fixing line wrapping because it can contain a '\n' n_stmt.prefix = node.prefix # add necessary imports if 'Raises' in method or 'Warns' in method: add_import('pytest', node) if ('Regex' in method and not 'Raises' in method and not 'Warns' in method): add_import('re', node) return n_stmt
def transform(self, node, results): if 'name' in results: # This matched an import statement. Fix that up: name = results["name"] name.replace(Name(self.DECORATOR_NAME, prefix=name.prefix)) if 'rename' in results: # The import statement use import as self._add_pattern("'%s'" % results['rename'].value) if 'interface_rename' in results: self._add_pattern("'%s' trailer< '.' '%s' > " % ( results['interface_rename'].value, self.FUNCTION_NAME)) if 'statement' in results: # This matched a class that has an <FUNCTION_NAME>(IFoo) statement. # We must convert that statement to a class decorator # and put it before the class definition. statement = results['statement'] if not isinstance(statement, list): statement = [statement] # Make a copy for insertion before the class: statement = [x.clone() for x in statement] # Get rid of leading whitespace: statement[0].prefix = '' # Rename function to decorator: if statement[-1].children: func = statement[-1].children[-1] else: func = statement[-1] if func.value == self.FUNCTION_NAME: func.value = self.DECORATOR_NAME interface = results['interface'] if not isinstance(interface, list): interface = [interface] interface = [x.clone() for x in interface] # Create the decorator: decorator = Node(syms.decorator, [Leaf(50, '@'),] + statement + [Leaf(7, '(')] + interface + [Leaf(8, ')')]) # Take the current class constructor prefix, and stick it into # the decorator, to set the decorators indentation. nodeprefix = node.prefix decorator.prefix = nodeprefix # Preserve only the indent: if '\n' in nodeprefix: nodeprefix = nodeprefix[nodeprefix.rfind('\n')+1:] # Then find the last line of the previous node and use that as # indentation, and add that to the class constructors prefix. previous = node.prev_sibling if previous is None: prefix = '' else: prefix = str(previous) if '\n' in prefix: prefix = prefix[prefix.rfind('\n')+1:] prefix = prefix + nodeprefix if not prefix or prefix[0] != '\n': prefix = '\n' + prefix node.prefix = prefix new_node = Node(syms.decorated, [decorator, node.clone()]) # Look for the actual function calls in the new node and remove it. for node in new_node.post_order(): for pattern in self.function_patterns: if pattern.match(node, results): parent = node.parent previous = node.prev_sibling # Remove the node node.remove() if not str(parent).strip(): # This is an empty class. Stick in a pass if (len(parent.children) < 3 or ' ' in parent.children[2].value): # This class had no body whitespace. parent.insert_child(2, Leaf(0, ' pass')) else: # This class had body whitespace already. parent.insert_child(2, Leaf(0, 'pass')) parent.insert_child(3, Leaf(0, '\n')) elif (prefix and isinstance(previous, Leaf) and '\n' not in previous.value and previous.value.strip() == ''): # This is just whitespace, remove it: previous.remove() return new_node
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() kwargs[name.value] = value if '\n' in arg.prefix: value.prefix = arg.prefix else: value.prefix = arg.prefix.strip() + " " 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'], node=node) 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'])) def fix_line_wrapping(x): for c in x.children: # no need to worry about wrapping of "[", "{" and "(" if c.type in [token.LSQB, token.LBRACE, token.LPAR]: break if c.prefix.startswith('\n'): c.prefix = c.prefix.replace('\n', ' \\\n') fix_line_wrapping(c) fix_line_wrapping(n_stmt) # the prefix should be set only after fixing line wrapping because it can contain a '\n' n_stmt.prefix = node.prefix # add necessary imports if 'Raises' in method or 'Warns' in method: add_import('pytest', node) if 'Regex' in method: add_import('re', node) return n_stmt