def _if_gt(value): if value >= 0: comparators = [Constant(value=value, kind=None)] else: comparators = [ UnaryOp( op=USub(), operand=Constant(value=abs(value), kind=None), ), ] return [ If( test=Compare( left=Call( func=Name(id='int', ctx=Load()), args=[Name(id='value', ctx=Load())], keywords=[], ), ops=[Gt()], comparators=comparators, ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ) ]
def _if_pattern(self, pattern): self.imported.add('re') # fix known ietf regex use pattern = pattern.replace('\\p{N}\\p{L}', '\\w') return [ If( test=UnaryOp( op=Not(), operand=Call( func=Attribute( value=Name(id='re', ctx=Load()), attr='match', ctx=Load(), ), args=[ Constant(value=pattern, kind=None), Name(id='value', ctx=Load()), Attribute( value=Name(id='re', ctx=Load()), attr='UNICODE', ctx=Load(), ), ], keywords=[], ), ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ), ]
def test_ast_tree_edit_distance() -> None: node1 = ast.parse("a=1") node2 = ast.parse("a=1") assert ast_tree_edit_distance(node1, node2) == 0 node2 = ast.parse("b=1") assert ast_tree_edit_distance(node1, node2) == 1 node2 = ast.parse("b=2") assert ast_tree_edit_distance(node1, node2) == 1.5 node1 = Assign( targets=[Name(id="a", ctx=Store())], value=Constant(value=1, kind=None), type_comment=None, ) node2 = Assign( targets=[Name(id="a", ctx=Store())], value=Constant(value=1, kind=None), type_comment=None, ) assert ast_tree_edit_distance(node1, node2) == 0 node1 = ast.parse("") node2 = ast.parse("") assert ast_tree_edit_distance(node1, node2) == 0 node1 = ast.parse("a") node2 = ast.parse("a") assert ast_tree_edit_distance(node1, node2) == 0 node1 = Expr(Name("a", Load())) node2 = Expr(Name("a", Load())) assert ast_tree_edit_distance(node1, node2) == 0 node1 = Name("a", Load()) node2 = Name("a", Load()) assert ast_tree_edit_distance(node1, node2) == 0
def load_helpers(self) -> Iterable[stmt]: helpers = [] if self.track_import_call: helpers.append( lineinfo( make_assign( [lineinfo(ast.Name("<strict-modules>", ast.Store()))], lineinfo( ast.Subscript( lineinfo(ast.Name("<fixed-modules>", ast.Load())), lineinfo(ast.Index(lineinfo(Constant("__strict__")))), ast.Load(), ) ), ) ) ) helpers.append( lineinfo( make_assign( [lineinfo(ast.Name("<track-import-call>", ast.Store()))], lineinfo( ast.Subscript( lineinfo(ast.Name("<strict-modules>", ast.Load())), lineinfo( ast.Index(lineinfo(Constant("track_import_call"))) ), ast.Load(), ) ), ) ), ) return helpers
def visit_Name(self, node): if isinstance(node.ctx, Store): return self.make_node( node, "storeName", values=[copy_loc(node, Constant(value=node.id))] ) else: return self.make_node( node, "name", values=[copy_loc(node, Constant(value=node.id))] )
def d_identifier_or_constant(t): ''' identifier_or_constant : "[a-zA-Z0-9]+" instmode ''' # you need to use double quotes here! if t[0].lower() == "true" or t[0] == "1": return Constant("True", instmode=t[1]) elif t[0].lower() == "false" or t[0] == "0": return Constant("False", instmode=t[1]) else: return Identifier(t[0], instmode=t[1])
def getScopeTracer(file_name, method_name, id): # name = _lamba_0 return Expr( value=Call( func=Attribute( value=Name(id='Tracer', ctx=Load()), attr='traceScope', ctx=Load() ), args=[ Constant(value=str(file_name), kind=None), Constant(value=str(id), kind=None), Name(id=method_name + '_locals', ctx=Load()) ], keywords=[] ) )
def visit_FunctionDef(self, node, drop_decorator=False): if node.args.posonlyargs: raise WormSyntaxError( "Worm does not support positional only arguments.", at=get_loc(self.filename, node) ) if node.args.kwonlyargs: raise WormSyntaxError( "Worm does not support keyword only arguments.", at=get_loc(self.filename, node) ) args = copy_loc( node, List(elts=list(map(self.visit_arg, node.args.args)), ctx=Load()) ) defaults = copy_loc( node, List(elts=list(map(self.visit, node.args.defaults)), ctx=Load()) ) if len(node.body) > 0 and is_docstring(node.body[0]): d, *body = node.body docstring = d.value else: docstring = Constant(None) body = node.body func = self.make_node( node, "funcDef", values=[ copy_loc(node, Constant(node.name)), args, defaults, self.make_node( node, "block", values=[ copy_loc( node, List(elts=list(map(self.visit, body)), ctx=Load()), ) ], ), node.returns or copy_loc(node, Constant(None)), docstring, ], ) if drop_decorator: return func else: return reduce(compose_dec, reversed(node.decorator_list), func)
def _make_coverage_dummy_expr(macronode): """Force expression `macronode` to be reported as covered by coverage tools. This facilitates "deleting" expression nodes by `return None` from a macro. Since an expression slot in the AST cannot be empty, we inject a dummy node that evaluates to `None`. `macronode` is the macro invocation node to copy source location info from. """ # TODO: inject the macro name for human-readability # We inject a lambda and an immediate call to it, because a constant `None`, # if it appears alone in an `ast.Expr`, is optimized away by CPython. # We must set location info manually, because we run after `expand`. non = copy_location(Constant(value=None), macronode) lam = copy_location( Lambda(args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=non), macronode) call = copy_location(Call(func=lam, args=[], keywords=[]), macronode) return Done(call)
def visit_Module(self, node): logging.info("Adding " + str(len(self.globalvars)) + " global variables, " + str(len(self.methods)) + " methods") node_body = node.body node.body = [] for n in node_body: if type(n) is ast.ImportFrom or type(n) is ast.Import: node.body.append(n) node_body.remove(n) node.body.append( Import(names=[alias(name='sys', asname=None)]) ) node.body.append(Expr(value=Call(func=Attribute( value=Attribute(value=Name(id='sys', ctx=Load()), attr='path', ctx=Load()), attr='append', ctx=Load()), args=[Constant(value=path_to_tracer, kind=None)], keywords=[])) ) node.body.append(Import(names=[alias(name='Tracer', asname=None)])) for globalvar in self.globalvars: node.body.append(globalvar) for method in self.methods: node.body.append(method) node.body.extend(node_body) return node
def getTracer(file_name, arg_name, id): return Expr( value=Call( func=Attribute( value=Name(id='Tracer', ctx=Load()), attr='trace', ctx=Load()), args=[ Constant(value=str(file_name), kind=None), Constant(value=str(id), kind=None), Constant(value=str(arg_name), kind=None), Name(id=arg_name, ctx=Load()), ], keywords=[] ) )
def getLambdaMethod(methodname, body, args_node, file, id): # methodname = _lambda_0 global lambdaID fullbody = [] fullbody.extend(getTracers(file, args_node, id)) fullbody.append(getScopeTracer(file, methodname, id)) fullbody.append( Return( value=Call( func=Name(id='eval', ctx=Load()), args=[ Constant(value=to_source(body), kind=None), Call( func=Name(id='locals', ctx=Load()), args=[], keywords=[]), Name(id=methodname + '_locals', ctx=Load())], keywords=[] ) ) ) return FunctionDef( name=methodname + '_return', args=args_node, body=fullbody, decorator_list=[], returns=None, type_comment=None)
def ID(self, tree): if hasattr(tree, "value") and tree.value in ("true", "false"): from ast import Constant return Constant("true" == tree.value) return astlib.name(tree.value)
def _add_coverage_dummy_node(tree, macronode, macroname): '''Force `macronode` to be reported as covered by coverage tools. The dummy node will be injected to `tree`. The `tree` must appear in a position where `ast.NodeTransformer.visit` may return a list of nodes. `macronode` is the macro invocation node to copy source location info from. `macroname` is included in the dummy node, to ease debugging. ''' # `macronode` itself might be macro-generated. In that case don't bother. if not hasattr(macronode, 'lineno') and not hasattr( macronode, 'col_offset'): return tree if tree is None: tree = [] elif isinstance(tree, AST): tree = [tree] # The dummy node must actually run to get coverage, so an `ast.Pass` won't do. # It must *do* something, or CPython optimizes it away, so an `ast.Expr` won't do. # We must set location info manually, because we run after `expand`. v = copy_location( Constant( value=f"source line {macronode.lineno} invoked macro {macroname}"), macronode) t = copy_location(Name(id="_mcpyrate_coverage", ctx=Store()), macronode) dummy = copy_location(Assign(targets=[t], value=v), macronode) tree.insert( 0, Done(dummy) ) # mark as Done so any expansions further out won't mess this up. return tree
def _expand(self, syntax, target, macroname, tree, kw=None): """ Transform `target` node, replacing it with the expansion result of aplying the named macro on the proper node and recursively treat the expansion as well. """ macro = self.bindings[macroname] kw = kw or {} kw.update({ 'syntax': syntax, 'to_source': unparse, 'expand_macros': self.visit }) expansion = _apply_macro(macro, tree, kw) if syntax == 'block': # I'm not sure why is all this mess # # Strategy 1: Make the last line cover the whole block. # Result: Covers the "with document" line, but not the last one. # copy_location(expansion[-1], target) # # Strategy 2: Make the second line cover the whole block. # Result: Covers all, unless the block is just 2 lines. # copy_location(expansion[1], target) # Lo mejor para largo > 2 # # Strategy 3: Insert a second dummy line covering the whole block. # Result: Works dummy = Expr(value=Call(func=Name(id="id", ctx=Load()), args=[Constant(value="bogus", kind=None)], keywords=[]), lineno=target.lineno) copy_location(dummy, target) expansion.insert(1, dummy) expansion = self._visit_expansion(expansion, target) return expansion
def test_ast_equal() -> None: a = Name(id="print", ctx=Load()) b = Name(id="print", ctx=Load()) assert ast_deep_equal(a, b) a = Expr(value=Call( func=Name(id="print", ctx=Load()), args=[Constant(value="hello, world")], keywords=[], )) b = Expr(value=Call( func=Name(id="print", ctx=Load()), args=[Constant(value="hello, world")], keywords=[], )) assert ast_deep_equal(a, b)
def _attr_lookup(self, attr, value): if "__" not in attr: return Compare( left=Attribute(value=Name(id="self", **self.file), attr=attr, **self.file), ops=[Eq()], comparators=[self.build_expression(value)], **self.file, ) attr, lookup = attr.split("__", 1) if lookup == "isnull": return Compare( left=Attribute(value=Name(id="self", **self.file), attr=attr, **self.file), ops=[Is() if value else IsNot()], comparators=[ Constant(value=None, **self.file) # Name(id="None", **self.file) ], **self.file, ) if lookup == "exact": return self._attr_lookup(attr, value) raise ValueError("Unhandled attr lookup")
def set_value(value, kind=None): """ Creates a Constant or a Str depending on Python version in use :param value: AST node :type value: ```Any``` :param kind: AST node :type kind: ```Optional[Any]``` :return: Probably a string, but could be any constant value :rtype: ```Union[Constant, Str, NameConstant]``` """ if ( value is not None and isinstance(value, str) and len(value) > 2 and value[0] + value[-1] in frozenset(('""', "''")) ): value = value[1:-1] return ( Constant(kind=kind, value=value, constant_value=None, string=None) if PY_GTE_3_8 else ( Str(s=value, constant_value=None, string=None) if isinstance(value, str) else Num(n=value, constant_value=None, string=None) if not isinstance(value, bool) and isinstance(value, (int, float, complex)) else NameConstant(value=value, constant_value=None, string=None) ) )
def visit_FunctionDef(self, node): # if there is at least one decorator, it may be a worm one if node.decorator_list: # get the Worm AST try: undecorated_func = RewriteWorm(self.filename).visit_FunctionDef(node, drop_decorator=True) except Exception as e: msg = Constant(*e.args) lambda_worm_func = make_node(self.filename, node, "invalidNode", values=[msg]) else: undecorated_func.decorator_list = [] lambda_worm_func = make_lambda(undecorated_func) decorators = make_list( node.decorator_list[0], node.decorator_list ) apply = make_apply_decorator(decorators, lambda_worm_func) node.decorator_list = [apply] if len(node.body) > 0 and is_docstring(node.body[0]): docstring, *body = node.body node.body = [docstring, *map(self.visit, body)] else: node.body = list(map(self.visit, node.body)) return node
def _pattern(pattern): left, right = pattern if left in ("[]", "_"): right = e(f"lambda : {right}") else: right = e(f"lambda {left} : {right}") left = Constant(left) return Tuple(elts=[left, right], ctx=Load())
def visit_arg(self, node): params = [copy_loc(node, Constant(node.arg))] if node.annotation: annot = self.visit(node.annotation) if annot: params.append(annot) return self.make_node(node, "arg", values=params)
def make_cached_property_init_decorator( self, scope_data: ClassScope, class_scope: SymbolScope[TVar, TScopeData], ) -> expr: return lineinfo( ast.Call( lineinfo(ast.Name("<init-cached-properties>", ast.Load())), [ lineinfo( ast.Dict( [ lineinfo( Constant(mangle_priv_name(name, [class_scope])) ) for name in scope_data.cached_props ], [ lineinfo( ast.Tuple( [ lineinfo( Constant( mangle_priv_name( self.mangle_cached_prop(name), [class_scope], ) ) ), lineinfo(Constant(value)), ], ast.Load(), ) ) for name, value in scope_data.cached_props.items() ], ) ) ], [], ) )
def visit_Yield(self, node): existing_node = self.generic_visit(node) value = existing_node.value if value is None: if Constant is None: value = Name(id='None', ctx=Load()) else: value = Constant(value=None) return Yield(value=self._create_bare_context_call( 'yield_value', [value, Num(n=existing_node.lineno)]))
def mutate(self, node, index): root, new = node_copy_tree(node, index) value = eval(new.value) if isinstance(value, bytes): value = b"coffeebad" + value else: value = "mutated string " + value value = Constant(value=value, kind="") value = unparse(value).strip() new.value = value yield root, new
def test_get_value(self) -> None: """Tests get_value succeeds""" val = "foo" self.assertEqual(get_value(Str(s=val, constant_value=None, string=None)), val) self.assertEqual( get_value(Constant(value=val, constant_value=None, string=None)), val ) self.assertIsInstance(get_value(Tuple(expr=None)), Tuple) self.assertIsInstance(get_value(Tuple(expr=None)), Tuple) self.assertIsNone(get_value(Name(None, None))) self.assertEqual(get_value(get_value(ast.parse("-5").body[0])), -5)
def _make_product(terms): """ Return an AST expressing the product of all the terms. """ if terms: product = terms[0] for term in terms[1:]: product = BinOp(left=product, op=Mult(), right=term) return product else: return Constant(value=1)
def visit_Assign(self, n: Assign) -> Union[Assign, Tuple[Assign, Expr]]: assert len(n.targets) == 1 and isinstance(n.targets[0], Name) print_str = Call( Attribute( Constant(value=colored(f"{n.targets[0].id} = {{}}", "red")), attr="format", ctx=Load(), ), args=[Name(n.targets[0].id, Load())], keywords=[], ) return (n, self._fix_location(self._make_print([print_str]), n))
def visit_Attribute(self, node): if isinstance(node.ctx, Load): return self.make_node( node, "getAttr", values=[ self.visit(node.value), Constant(node.attr), ], ) elif isinstance(node.ctx, Store): return self.make_node( node, "setAttr", values=[ self.visit(node.value), Constant(node.attr), ], ) else: raise NotImplementedError()
def _make_print(self, ns: List[expr], prefix: str = None) -> Expr: # create the indent: ' ' * depth mul_by = Name(self._DEPTH_VAR, Load()) indent = BinOp(Constant(" "), Mult(), mul_by) # if prefix is given, indent is: ' ' * (depth - len(prefix)) + prefix if prefix is not None: assert len( prefix ) <= self._INDENT, f"too long {prefix} for given indent {self._INDENT}" indent.right = BinOp(mul_by, Sub(), Constant(len(prefix))) indent = BinOp(indent, Add(), Constant(prefix)) return Expr( Call( Name("print", Load()), args=cast(List[expr], [indent]) + ns, keywords=[ keyword("sep", Constant("")), keyword("file", Attribute(Name("sys", Load()), "stderr", Load())), ], ))
def visit_Assign(self, node): self.generic_visit(node) if not all(isinstance(tgt, Name) for tgt in node.targets): return node names = List(elts=[Constant(value=tgt.id) for tgt in node.targets], ctx=Load()) values = List( elts=[Name(id=tgt.id, ctx=Load()) for tgt in node.targets], ctx=Load()) return node, Expr( Call(func=Name(id="__assign__", ctx=Load()), args=[names, values], keywords=[]))