def visit_FunctionDef(self, node):
"""
Called when a FunctionDef node is visited. If decorators are found,
record them in self.decorators.
Parameters
----------
node : node
The node being visited
"""
self.decorators[node.name] = []
for n in node.decorator_list:
name = ''
if isinstance(n, ast.Call):
name = n.func.attr if isinstance(n.func, ast.Attribute) else n.func.id
else:
name = n.attr if isinstance(n, ast.Attribute) else n.id
self.decorators[node.name].append(name)
python类FunctionDef()的实例源码
def pythoneval(self, args, debug=False):
refs = [ast.Name("x{0}".format(i), ast.Load()) for i in xrange(len(args))]
if sys.version_info[0] <= 2:
params = ast.arguments([ast.Name("x{0}".format(i), ast.Param()) for i in xrange(len(args))], None, None, [])
fcn = ast.FunctionDef("tmp", params, [ast.Return(self.pythonast(refs))], [])
else:
params = ast.arguments([ast.arg("x{0}".format(i), None) for i in xrange(len(args))], None, [], [], None, [])
fcn = ast.FunctionDef("tmp", params, [ast.Return(self.pythonast(refs))], [], None)
moduleast = ast.Module([fcn])
fakeLineNumbers(moduleast)
if debug:
print(astToSource(fcn))
modulecomp = compile(moduleast, "Femtocode", "exec")
out = {"$importlib": importlib, "$math": math}
exec(modulecomp, out)
return out["tmp"](*args)
def _pos_in_src(self):
"""Return the position in source of the generated node."""
py_node = self.node.py_node
if py_node:
offset = getattr(py_node, 'col_offset', 0)
# multi-line comments have an offset of -1
if offset < 0:
offset = 0
# special handling of nodes that are decorable. Those nodes expose
# a 'lineno' that starts with the first decorator. for now, take
# the last decorator lineno and add one
if isinstance(py_node, (ast.FunctionDef, ast.AsyncFunctionDef,
ast.ClassDef)) and py_node.decorator_list:
result = (py_node.decorator_list[-1].lineno + 1, offset)
else:
result = (getattr(py_node, 'lineno', None),
offset)
else:
result = (None, None)
return result
def precheck(self, runner, script, info):
# create models dict if nonexistent
if getattr(script, 'functionDefs', None) is None:
script.functionDefs = {}
# create function argument stack if nonexistent
if getattr(script, 'functionArgs', None) is None:
script.functionArgs = []
# add new function definitions
if isinstance(info, ast.FunctionDef):
script.functionDefs[info.name] = info
script.functionArgs.append(info.args)
# update function def dictionary with assignments
elif isinstance(info, ast.Assign):
if isinstance(info.value, ast.Name):
if info.value.id in script.functionDefs:
for target in info.targets:
if isinstance(target, ast.Name):
script.functionDefs[target.id] = script.functionDefs[info.value.id]
else:
for target in info.targets:
if isinstance(target, ast.Name):
if target.id in script.functionDefs:
del script.functionDefs[target.id]
def run_in_context(code, context, defs={}):
ast_ = ast.parse(code, '<code>', 'exec')
last_expr = None
last_def_name = None
for field_ in ast.iter_fields(ast_):
if 'body' != field_[0]:
continue
if len(field_[1]) > 0:
le = field_[1][-1]
if isinstance(le, ast.Expr):
last_expr = ast.Expression()
last_expr.body = field_[1].pop().value
elif isinstance(le, (ast.FunctionDef, ast.ClassDef)):
last_def_name = le.name
exec(compile(ast_, '<hbi-code>', 'exec'), context, defs)
if last_expr is not None:
return eval(compile(last_expr, '<hbi-code>', 'eval'), context, defs)
elif last_def_name is not None:
return defs[last_def_name]
return None
def eval_function_def(function_def, globals_=None, flags=None):
"""
Evaluates an AST of a function definition with an optional dictionary of globals.
Returns a callable function (a ``types.FunctionType`` object).
"""
assert type(function_def) == ast.FunctionDef
# Making a copy before mutating
module = ast.Module(body=[copy.deepcopy(function_def)])
ast.fix_missing_locations(module)
if flags is not None:
kwds = dict(dont_inherit=True, flags=flags)
else:
kwds = {}
code_object = compile(module, '<nofile>', 'exec', **kwds)
locals_ = {}
eval(code_object, globals_, locals_)
return locals_[function_def.name]
def filter_function_def(function_def, bound_argnames):
"""
Filters a node containing a function definition (an ``ast.FunctionDef`` object)
to exclude all arguments with the names present in ``bound_arguments``.
Returns the new ``ast.arguments`` node.
"""
assert type(function_def) == ast.FunctionDef
new_args = filter_arguments(function_def.args, bound_argnames)
params = dict(
name=function_def.name,
args=new_args,
body=function_def.body,
decorator_list=function_def.decorator_list,
returns=function_def.returns)
return ast.FunctionDef(**params)
def function_from_source(source, globals_=None):
"""
A helper function to construct a Function object from a source
with custom __future__ imports.
"""
module = ast.parse(unindent(source))
ast.fix_missing_locations(module)
for stmt in module.body:
if type(stmt) == ast.FunctionDef:
tree = stmt
name = stmt.name
break
else:
raise ValueError("No function definitions found in the provided source")
code_object = compile(module, '<nofile>', 'exec', dont_inherit=True)
locals_ = {}
eval(code_object, globals_, locals_)
function_obj = locals_[name]
function_obj._peval_source = astunparse.unparse(tree)
return Function.from_object(function_obj)
def translate_FunctionDef(self, ctx, tree):
argument_translation = ast.arguments(
args=[
ast.Name(id=arg.uniq_id)
for arg in tree.args.args
],
vararg=None,
kwarg=None,
defaults=[])
body_block = tree.body_block
body_block_translation = body_block.translate_return(ctx)
return ast.FunctionDef(
name=tree.uniq_id,
args=argument_translation,
body=body_block_translation,
decorator_list=[])
def check_for_b901(self, node):
xs = list(node.body)
has_yield = False
return_node = None
while xs:
x = xs.pop()
if isinstance(x, (ast.AsyncFunctionDef, ast.FunctionDef)):
continue
elif isinstance(x, (ast.Yield, ast.YieldFrom)):
has_yield = True
elif isinstance(x, ast.Return) and x.value is not None:
return_node = x
if has_yield and return_node is not None:
self.errors.append(
B901(return_node.lineno, return_node.col_offset)
)
break
xs.extend(ast.iter_child_nodes(x))
def get_object_name(obj):
"""
Return the name of a given object
"""
name_dispatch = {
ast.Name: "id",
ast.Attribute: "attr",
ast.Call: "func",
ast.FunctionDef: "name",
ast.ClassDef: "name",
ast.Subscript: "value",
}
# This is a new ast type in Python 3
if hasattr(ast, "arg"):
name_dispatch[ast.arg] = "arg"
while not isinstance(obj, str):
assert type(obj) in name_dispatch
obj = getattr(obj, name_dispatch[type(obj)])
return obj
def add_contract(code):
_defs = []
for item in code:
# Function definitions
if isinstance(item, ast.FunctionDef):
_defs.append(item)
else:
raise StructureException("Invalid contract reference", item)
return _defs
def get_func_nodes(self):
"""Get all nodes from a function."""
return [definition for definition in project_definitions.values() if isinstance(definition.node, ast.FunctionDef)]
def method_to_ast(method: types.MethodType, file: str = None) -> ast.FunctionDef:
"""
Return node object for method.
"""
if method and not isinstance(method, types.MethodType):
raise TypeError('Unexpected type: {}'.format(str(type(method))))
result = None
if method and hasattr(method, '__code__'):
result = code_to_ast(method.__code__, file)
return result
def ast_to_func(node: ast.AST, old_func: types.FunctionType, file: str = None) -> types.FunctionType:
"""
Compile node object to function.
"""
if node and not isinstance(node, (ast.Module, ast.FunctionDef)):
raise TypeError('Unexpected type for node: {}'.format(str(type(node))))
if old_func and not isinstance(old_func, types.FunctionType):
raise TypeError('Unexpected type for old_func: {}'.format(str(type(old_func))))
result = old_func
if node and old_func:
old_code = getattr(old_func, '__code__', None)
if not isinstance(node, ast.Module):
mod_node = ast.copy_location(ast.Module(body = [node]), node)
fun_node = node
else:
mod_node = node
fun_node = node.body[0]
module = ast_to_code(mod_node, old_code, file=file)
for code in module.co_consts:
if not isinstance(code, types.CodeType):
continue
if code.co_name == fun_node.name and code.co_firstlineno == fun_node.lineno:
result.__code__ = code
break
else:
raise ValueError('Not specified value')
return result
def contains_function(a, problem_name):
for line in a.body:
if type(line) == ast.FunctionDef and line.name == problem_name:
return True
return False
def updateVariableNames(a, varMap, scopeName, randomCounter, imports):
if not isinstance(a, ast.AST):
return
if type(a) in [ast.FunctionDef, ast.ClassDef]:
if a.name in varMap:
if not hasattr(a, "originalId"):
a.originalId = a.name
a.name = varMap[a.name]
anonymizeStatementNames(a, varMap, "_" + a.name, imports)
elif type(a) == ast.arg:
if a.arg not in varMap and not (builtInName(a.arg) or importedName(a.arg, imports)):
log("Can't assign to arg?", "bug")
if a.arg in varMap:
if not hasattr(a, "originalId"):
a.originalId = a.arg
if varMap[a.arg][0] == "r":
a.randomVar = True # so we know it can crash
if a.arg == varMap[a.arg]:
# Check whether this is a given name
if not isAnonVariable(varMap[a.arg]):
a.dontChangeName = True
a.arg = varMap[a.arg]
elif type(a) == ast.Name:
if a.id not in varMap and not (builtInName(a.id) or importedName(a.id, imports)):
varMap[a.id] = "r" + str(randomCounter[0]) + scopeName
randomCounter[0] += 1
if a.id in varMap:
if not hasattr(a, "originalId"):
a.originalId = a.id
if varMap[a.id][0] == "r":
a.randomVar = True # so we know it can crash
if a.id == varMap[a.id]:
# Check whether this is a given name
if not isAnonVariable(varMap[a.id]):
a.dontChangeName = True
a.id = varMap[a.id]
else:
for child in ast.iter_child_nodes(a):
updateVariableNames(child, varMap, scopeName, randomCounter, imports)
def anonymizeStatementNames(a, globalMap, scopeName, imports, goBackwards=False):
"""Gather the local variables, then update variable names in each line"""
localMap = gatherLocalScope(a, globalMap, scopeName, imports, goBackwards=goBackwards)
varMap = { }
varMap.update(globalMap)
varMap.update(localMap)
randomCounter = [0]
functionsSeen = []
if type(a) == ast.FunctionDef:
for arg in a.args.args:
updateVariableNames(arg, varMap, scopeName, randomCounter, imports)
for line in a.body:
updateVariableNames(line, varMap, scopeName, randomCounter, imports)
def staticVars(l, vars):
"""Determines whether the given lines change the given variables"""
# First, if one of the variables can be modified, there might be a problem
mutableVars = []
for var in vars:
if (not (hasattr(var, "type") and (var.type in [int, float, str, bool]))):
mutableVars.append(var)
for i in range(len(l)):
if type(l[i]) == ast.Assign:
for var in vars:
if var.id in allVariableNamesUsed(l[i].targets[0]):
return False
elif type(l[i]) == ast.AugAssign:
for var in vars:
if var.id in allVariableNamesUsed(l[i].target):
return False
elif type(l[i]) in [ast.If, ast.While]:
if not (staticVars(l[i].body, vars) and staticVars(l[i].orelse, vars)):
return False
elif type(l[i]) == ast.For:
for var in vars:
if var.id in allVariableNamesUsed(l[i].target):
return False
if not (staticVars(l[i].body, vars) and staticVars(l[i].orelse, vars)):
return False
elif type(l[i]) in [ast.FunctionDef, ast.ClassDef, ast.Try, ast.With]:
log("transformations\tstaticVars\tMissing type: " + str(type(l[i])), "bug")
# If a mutable variable is used, we can't trust it
for var in mutableVars:
if var.id in allVariableNamesUsed(l[i]):
return False
return True
def isStatement(a):
"""Determine whether the given node is a statement (vs an expression)"""
return type(a) in [ ast.Module, ast.Interactive, ast.Expression, ast.Suite,
ast.FunctionDef, ast.ClassDef, ast.Return, ast.Delete,
ast.Assign, ast.AugAssign, ast.For, ast.While,
ast.If, ast.With, ast.Raise, ast.Try,
ast.Assert, ast.Import, ast.ImportFrom, ast.Global,
ast.Expr, ast.Pass, ast.Break, ast.Continue ]