def visit_Call(self, node): node = self.generic_visit(node) if not isinstance(node.func, ast.Name): # only direct calls, we cannot detect them otherwise return node if node.func.id_scope != Scope.GLOBAL: # `locals` is a python built-in. return node if node.func.id != "locals": return node block = self._stack[-1] def condition(var, scope): if naming.decode_id(var)[1]: # no variables we introduced return False elif var.startswith("__pydron"): return False elif scope == Scope.LOCAL or scope == Scope.SHARED: # local variables are welcome return True elif not isinstance(block, ast.ClassDef): # free variables too... return scope == Scope.FREE else: # ... unless we are in a class return False localvars = set([var for var,scope in block.scopes.iteritems() if condition(var, scope)]) args = mk_dict((mk_str(var), mk_call('__pydron_unbound_unchecked__', [mk_name(var)])) for var in localvars) return mk_call('__pydron_locals__', [args])
def visit_FunctionDef(self, node): self.generic_visit(node) scopes = node.scopes shared = [var for var,scope in scopes.iteritems() if scope == scoping.Scope.SHARED] free = [var for var,scope in scopes.iteritems() if scope == scoping.Scope.FREE] # create cells first thing in body shared_stmts = [mk_assign(var, mk_call("__pydron_new_cell__", [mk_str(var)])) for var in shared] node.body = shared_stmts + node.body if free: # free vars become arguments free_args = [ast.Name(id=var, ctx=ast.Param()) for var in free] node.args.args = free_args + node.args.args # rename the function orig_name = node.name tmp_name = self.id_factory(node.name) node.name = tmp_name # wrap it if self._inside_class(): wrap_args = [mk_name(naming.passthrough_var(var)) for var in free] else: wrap_args = [mk_name(var) for var in free] wrap_call = mk_call("__pydron_wrap_closure__", [mk_name(tmp_name), mk_tuple(wrap_args)]) wrap_stmt = mk_assign(orig_name, wrap_call) return [node, wrap_stmt] else: return node
def unpack_star(self, module_id): tmp = self._unique_modattr_id() dir_expr = mk_call("dir", args=[mk_name(module_id)]) all_expr = mk_call("getattr", args=[mk_name(module_id), mk_str('__all__'), dir_expr]) value_expr = mk_call("getattr", args=[mk_name(module_id), mk_name(tmp)]) body_stmt = mk_subscript_assign(mk_call("globals"), mk_name(tmp), value_expr) return ast.For(target=mk_name(tmp), iter=all_expr, body=[body_stmt], orelse=[])
def visit_Exec(self, node): if not node.locals and not node.globals: l = mk_call("locals") g = mk_call("globals") else: l = node.locals g = node.globals if l is None: l = mk_name("None") if g is None: g = mk_name("None") return ast.Expr(value=mk_call("__pydron_exec__", args=[node.body, l, g]))
def visit_Exec(self, node): if not node.locals and not node.globals: l = mk_call("locals") g = mk_call("globals") else: l = node.locals g = node.globals if l is None: l = mk_name("None") if g is None: g = mk_name("None") return ast.Expr( value=mk_call("__pydron_exec__", args=[node.body, l, g]))
def import_call_expr(self, name, fromlist=None, level=None): name_expr = mk_str(name) if name else mk_None() if fromlist: from_expr = mk_tuple(mk_str(item) for item in fromlist) else: from_expr = mk_None() args = [name_expr, mk_call("globals"), mk_None(), from_expr] if level: args.append(mk_num(level)) return mk_call("__import__", args)
def visit_FunctionDef(self, node): args = node.args if not args.defaults: return node orig_id = node.name node.name = self.id_factory(orig_id) parameters = mk_tuple(mk_str(arg.id) for arg in args.args) defaults = mk_tuple(args.defaults) parameters_id = self.id_factory("tuple") parameters_stmt = mk_assign(parameters_id, parameters) parameters = mk_name(parameters_id) defaults_id = self.id_factory("tuple") default_stmt = mk_assign(defaults_id, defaults) defaults = mk_name(defaults_id) args.defaults = [] func_expr = mk_call("__pydron_defaults__", [mk_name(node.name), parameters, defaults]) assign_stmt = mk_assign(orig_id, func_expr) return [node, parameters_stmt, default_stmt, assign_stmt]
def visit_Name(self, node): if isinstance(node.ctx, ast.Load) and not node.id.startswith( "__pydron" ) and not node.id == "True" and not node.id == "False" and not node.id == "None": return mk_call('__pydron_unbound_check__', [node]) else: return node
def visit_GeneratorExp(self, node): # first build the function that yields the elements. # we don't to any depth traversal here to attempt to # simply expressions. We do this later on the # function. yieldstmt = ast.Expr() yieldstmt.value = ast.Yield() yieldstmt.value.value = node.elt body = [yieldstmt] for generator in reversed(node.generators): loop = self.build_comprehension(generator, body) body = [loop] func = ast.FunctionDef() funcname = self.id_factory("generator") func.name = funcname args = ast.arguments() args.args = [] args.vararg = None args.kwarg = None args.defaults = [] func.args = args func.decorator_list = [] func.body = body # Now simplify all expressions inside the generator. # We can do this now as we have a proper function statements = self.visit(func) self.execute(statements) return self.visit(mk_call(funcname))
def visit_FunctionDef(self, node): self.generic_visit(node) scopes = node.scopes shared = [ var for var, scope in scopes.iteritems() if scope == scoping.Scope.SHARED ] free = [ var for var, scope in scopes.iteritems() if scope == scoping.Scope.FREE ] # create cells first thing in body shared_stmts = [ mk_assign(var, mk_call("__pydron_new_cell__", [mk_str(var)])) for var in shared ] node.body = shared_stmts + node.body if free: # free vars become arguments free_args = [ast.Name(id=var, ctx=ast.Param()) for var in free] node.args.args = free_args + node.args.args # rename the function orig_name = node.name tmp_name = self.id_factory(node.name) node.name = tmp_name # wrap it if self._inside_class(): wrap_args = [ mk_name(naming.passthrough_var(var)) for var in free ] else: wrap_args = [mk_name(var) for var in free] wrap_call = mk_call( "__pydron_wrap_closure__", [mk_name(tmp_name), mk_tuple(wrap_args)]) wrap_stmt = mk_assign(orig_name, wrap_call) return [node, wrap_stmt] else: return node
def visit_For(self, node): node = self.generic_visit(node) iterator_id = self.id_factory("iterator") iter_stmt = mk_assign(iterator_id, mk_call("__pydron_iter__", [node.iter])) test = mk_call("__pydron_hasnext__", [mk_name(iterator_id)]) lhs = ast.Tuple(elts=[node.target, ast.Name(id=iterator_id, ctx=ast.Store())], ctx=ast.Store()) next_stmt = ast.Assign(targets=[lhs], value=mk_call("__pydron_next__", [mk_name(iterator_id)])) node.body.insert(0, next_stmt) while_stmt = ast.While(test=test, body=node.body, orelse=node.orelse) ast.copy_location(while_stmt, node) return [iter_stmt, while_stmt]
def unpack_star(self, module_id): tmp = self._unique_modattr_id() dir_expr = mk_call("dir", args=[mk_name(module_id)]) all_expr = mk_call( "getattr", args=[mk_name(module_id), mk_str('__all__'), dir_expr]) value_expr = mk_call("getattr", args=[mk_name(module_id), mk_name(tmp)]) body_stmt = mk_subscript_assign(mk_call("globals"), mk_name(tmp), value_expr) return ast.For(target=mk_name(tmp), iter=all_expr, body=[body_stmt], orelse=[])
def visit_Name(self, node): if self.inside_module(): return node if not self.is_global_name(node): return node if isinstance(node.ctx, ast.Load): return mk_call("__pydron_read_global__", args=[mk_str(node.id)]) else: raise ValueError("Unhandled assignment to a global variable: %s." % node.id)
def visit_Assert(self, node): node = self.generic_visit(node) if node.msg: inst = mk_call("AssertionError", [node.msg]) raise_stmt = ast.Raise(type=inst, inst=None, tback=None) else: raise_stmt = ast.Raise(type=mk_name("AssertionError"), inst=None, tback=None) check = ast.If(test=mk_not(node.test), body=[raise_stmt], orelse=[]) return ast.If(test=mk_name("__debug__"), body=[check], orelse=[])
def visit_Delete(self, node): if self.inside_module(): return node statements = [] for target in node.targets: if isinstance(target, ast.Name) and self.is_global_name(target): stmt = mk_call("__pydron_delete_global__", args=[mk_str(target.id)]) statements.append(ast.Expr(stmt)) else: statements.append(ast.Delete(targets=[target])) return statements
def visit_ExceptHandler(self, node): if self.inside_module(): return node if isinstance(node.name, ast.Name) and self.is_global_name(node.name): var = node.name.id stmt = mk_call("__pydron_assign_global__", args=[mk_str(var), mk_name(var)]) node.body = [ast.Expr(stmt)] + node.body elif node.name: node.name = self.visit(node.name) if node.type: node.type = self.visit(node.type) return node
def visit_Print(self, node): node = self.generic_visit(node) if node.dest: dest = node.dest else: dest = mk_name("None") if node.nl: nl = mk_name("True") else: nl = mk_name("False") values = mk_tuple(node.values) call = mk_call("__pydron_print__", [dest, values, nl]) return ast.Expr(value=call)
def visit_FunctionDef(self, node): self.stack.append(node) node = self.generic_visit(node) self.stack.pop() if self.inside_module(): return node if self.is_global(node, "name"): orig_name = node.name new_name = self.id_factory(orig_name) node.name = new_name stmt = mk_call("__pydron_assign_global__", args=[mk_str(orig_name), mk_name(new_name)]) return [node, ast.Expr(stmt)] else: return node
def visit_Slice(self, node): self.generic_visit(node) # ast.Slice becomes ast.Index with a slice() call. lower = node.lower upper = node.upper step = node.step if not lower: lower = mk_name("None") if not upper: upper = mk_name("None") if not step: step = mk_name("None") s = mk_call("slice", [lower, upper, step]) return ast.Index(value=s)
def visit_Assign(self, node): node.value = self.generic_visit(node.value) if self.inside_module(): return node statements = [] if len(node.targets) == 1: target = node.targets[0] if isinstance(target, ast.Name) and self.is_global_name(target): stmt = mk_call("__pydron_assign_global__", args=[mk_str(target.id), node.value]) statements.append(ast.Expr(value=stmt)) else: statements.append(node) else: raise ValueError("not supported") return statements
def visit_TryFinally(self, node): body, body_interrupts, _ = self.process_body(node.body) finalbody, finalbody_interrupts, _ = self.process_body(node.finalbody) orig_flag = self.id_factory("interrupted") # The finalbody statements are executed even if an interrupt was triggered. # Those statements might overwrite or reset the flag. We store # the original flag in a new variable so that we can 'merge' the two # flags afterwards. store = mk_assign(orig_flag, mk_name(self.flag_id)) finalbody.insert(0, store) # pick the flag with higher priority: # None < continue < break < return maxflag = mk_call('__pydron_max__', [mk_name(self.flag_id), mk_name(orig_flag)]) finalbody.append(mk_assign(self.flag_id, maxflag)) node.body = body node.finalbody = finalbody return node, body_interrupts | finalbody_interrupts, set()
def visit_ClassDef(self, node): node = self.generic_visit(node) # free variables of the class block become parameters of the function scopes = getattr(node, "scopes", {}) free = [var for var, scope in scopes.iteritems() if scope == scoping.Scope.FREE] free_param = [ast.Name(id=var, ctx=ast.Param()) for var in free] free_args = [ast.Name(id=var, ctx=ast.Load()) for var in free] # free variables of sub-blocks. Those need name mangling to avoid # collisions with variables local to the class block. passthrough = self.find_passthrough_vars(node) pt_param = [ast.Name(id=naming.passthrough_var(var), ctx=ast.Param()) for var in passthrough] pt_args = [ast.Name(id=var, ctx=ast.Load()) for var in passthrough] # function to execute the class body and collect the attributes func = ast.FunctionDef() func.name = self.id_factory("class_" + node.name) func.args = ast.arguments(args=free_param + pt_param, vararg=None, kwarg=None, defaults=[]) func.body = node.body + [ast.Return(value=mk_name("__pydron_members__"))] func.decorator_list = [] # replicate name mangling of `LocalizeFreeVariables` all_args = free_args + pt_args if self._inside_class(): for arg in all_args: arg.id = naming.passthrough_var(arg.id) # create the class typefunc = mk_call("__pydron_read_global__", [mk_str("type")]) class_expr = mk_call_expr(typefunc, [mk_str(node.name), mk_tuple(node.bases), mk_call(func.name, all_args)]) for decorator in reversed(node.decorator_list): class_expr = mk_call_expr(decorator, [class_expr]) stmt = mk_assign(node.name, class_expr) return [func, stmt]
def visit_ClassDef(self, node): node = self.generic_visit(node) # free variables of the class block become parameters of the function scopes = getattr(node, 'scopes', {}) free = [var for var,scope in scopes.iteritems() if scope == scoping.Scope.FREE] free_param = [ast.Name(id=var, ctx=ast.Param()) for var in free] free_args = [ast.Name(id=var, ctx=ast.Load()) for var in free] # free variables of sub-blocks. Those need name mangling to avoid # collisions with variables local to the class block. passthrough = self.find_passthrough_vars(node) pt_param = [ast.Name(id=naming.passthrough_var(var), ctx=ast.Param()) for var in passthrough] pt_args = [ast.Name(id=var, ctx=ast.Load()) for var in passthrough] # function to execute the class body and collect the attributes func = ast.FunctionDef() func.name = self.id_factory("class_" + node.name) func.args = ast.arguments(args=free_param + pt_param, vararg=None, kwarg=None, defaults=[]) func.body = node.body + [ast.Return(value=mk_name('__pydron_members__')) ] func.decorator_list = [] # replicate name mangling of `LocalizeFreeVariables` all_args = free_args + pt_args if self._inside_class(): for arg in all_args: arg.id = naming.passthrough_var(arg.id) # create the class typefunc = mk_call('__pydron_read_global__', [mk_str('type')]) class_expr = mk_call_expr(typefunc, [mk_str(node.name), mk_tuple(node.bases), mk_call(func.name, all_args)]) for decorator in reversed(node.decorator_list): class_expr = mk_call_expr(decorator, [class_expr]) stmt = mk_assign(node.name, class_expr) return [func, stmt]
def visit_TryFinally(self, node): body, body_interrupts, _ = self.process_body(node.body) finalbody, finalbody_interrupts, _ = self.process_body(node.finalbody) orig_flag = self.id_factory("interrupted") # The finalbody statements are executed even if an interrupt was triggered. # Those statements might overwrite or reset the flag. We store # the original flag in a new variable so that we can 'merge' the two # flags afterwards. store = mk_assign(orig_flag, mk_name(self.flag_id)) finalbody.insert(0, store) # pick the flag with higher priority: # None < continue < break < return maxflag = mk_call( '__pydron_max__', [mk_name(self.flag_id), mk_name(orig_flag)]) finalbody.append(mk_assign(self.flag_id, maxflag)) node.body = body node.finalbody = finalbody return node, body_interrupts | finalbody_interrupts, set()
def visit_ClassDef(self, node): self.generic_visit(node) stmt = mk_assign('__pydron_members__', mk_call('locals')) node.body.append(stmt) return node
def visit_With(self, node): node = self.generic_visit(node) # Build equivalent code with try catch finally. # PEP-0343 provides the equivalent code for us. statements = [] # mgr = node.expr mgr_id = self.id_factory("mgr") s = mk_assign(mgr_id, node.context_expr) statements.append(s) # exit = type(msg).__exit__ exit_id = self.id_factory("exit") s = mk_assign(exit_id, mk_attr(mk_call('type', [mk_name(mgr_id)]), "__exit__")) statements.append(s) # value = type(msg).__enter__(mgr) value_id = self.id_factory("value") s = mk_assign( value_id, mk_call_expr( mk_attr(mk_call('type', [mk_name(mgr_id)]), "__enter__"), [mk_name(mgr_id)])) statements.append(s) # exc = True exc_id = self.id_factory("exc") s = mk_assign(exc_id, mk_name("True")) statements.append(s) # try: tryfinally_body = [] tryfinally_finalbody = [] s = ast.TryFinally(body=tryfinally_body, finalbody=tryfinally_finalbody) statements.append(s) # try: tryexcept_body = [] tryexcept_except = [] expt_handler = ast.ExceptHandler(type=None, name=None, body=tryexcept_except) s = ast.TryExcept(body=tryexcept_body, handlers=[expt_handler], orelse=[]) tryfinally_body.append(s) # node.optional_vars = value if node.optional_vars: s = ast.Assign(targets=[node.optional_vars], value=mk_name(value_id)) tryexcept_body.append(s) # body tryexcept_body.extend(node.body) # except: # exc = False s = mk_assign(exc_id, mk_name("False")) tryexcept_except.append(s) # sys.exc_info() sys_exc_info = mk_call_expr(mk_attr(mk_name('sys'), "exc_info"), []) # exit(mgr, *sys.exc_info()) exit_call = mk_call(exit_id, [mk_name(mgr_id)], vararg=sys_exc_info) # not exit(mgr, *sys.exc_info()) test = ast.UnaryOp(op=ast.Not(), operand=exit_call) # if not exit(mgr, *sys.exc_info()): # raise s = ast.If(test=test, body=[ast.Raise(type=None, inst=None, tback=None)], orelse=[]) tryexcept_except.append(s) # finally: # if exc: # exit(mgr, None, None, None) exit_call = mk_call(exit_id, [ mk_name(mgr_id), mk_name("None"), mk_name("None"), mk_name("None") ]) s = ast.If(test=mk_name(exc_id), body=[ast.Expr(value=exit_call)], orelse=[]) tryfinally_finalbody.append(s) return statements
def visit_SetComp(self, node): node = self.generic_visit(node) listexpr = ast.ListComp(elt=node.elt, generators=node.generators) return mk_call('set', [listexpr])
def visit_Name(self, node): if isinstance(node.ctx, ast.Load) and not node.id.startswith("__pydron") and not node.id == "True" and not node.id == "False" and not node.id == "None": return mk_call('__pydron_unbound_check__', [node]) else: return node
def visit_FunctionDef(self, node): self.generic_visit(node) scopes = node.scopes shared = [ var for var, scope in scopes.iteritems() if scope == scoping.Scope.SHARED ] free = [ var for var, scope in scopes.iteritems() if scope == scoping.Scope.FREE ] # create cells first thing in body shared_stmts = [ mk_assign(var, mk_call("__pydron_new_cell__", [mk_str(var)])) for var in shared ] fixed_arguments = [] for arg in node.args.args: if isinstance(arg, ast.Attribute) and arg.attr == "cell_contents": # the generic_visit above made a mess. The attribute is # a free/shared variable and visit_Name replaced it with # an attribute. This makes no sense, so lets repair the damage. # # We don't want to change the parameter name. And I don't # want to change the variable name either. To solve this # we temporarily assign it to another variable: # # def foo(freevar): # freevar__U0 = freevar # freevar = __pydron_new_cell__('freevar') # freevar.cell_contents = freevar__U0 varname = arg.value.id tmp = self.id_factory(varname) # freevar__U0 = freevar shared_stmts.insert(0, mk_assign(tmp, mk_name(varname))) # freevar = __pydron_new_cell__('freevar') # is already in shared_stmts # reevar.cell_contents = freevar__U0 arg.ctx = ast.Store() shared_stmts.append( ast.Assign(targets=[arg], value=mk_name(tmp))) fixed_arguments.append(ast.Name(id=varname, ctx=ast.Param())) else: fixed_arguments.append(arg) node.args.args = fixed_arguments node.body = shared_stmts + node.body if free: # free vars become arguments free_args = [ast.Name(id=var, ctx=ast.Param()) for var in free] node.args.args = free_args + node.args.args # rename the function orig_name = node.name tmp_name = self.id_factory(node.name) node.name = tmp_name # wrap it if self._inside_class(): wrap_args = [ mk_name(naming.passthrough_var(var)) for var in free ] else: wrap_args = [mk_name(var) for var in free] wrap_call = mk_call( "__pydron_wrap_closure__", [mk_name(tmp_name), mk_tuple(wrap_args)]) wrap_stmt = mk_assign(orig_name, wrap_call) return [node, wrap_stmt] else: return node
def visit_DictComp(self, node): node = self.generic_visit(node) elt = mk_tuple([node.key, node.value]) listexpr = ast.ListComp(elt=elt, generators=node.generators) return mk_call('dict', [listexpr])
def visit_With(self, node): node = self.generic_visit(node) # Build equivalent code with try catch finally. # PEP-0343 provides the equivalent code for us. statements = [] # mgr = node.expr mgr_id = self.id_factory("mgr") s = mk_assign(mgr_id, node.context_expr) statements.append(s) # exit = type(msg).__exit__ exit_id = self.id_factory("exit") s = mk_assign(exit_id, mk_attr(mk_call('type', [mk_name(mgr_id)]), "__exit__")) statements.append(s) # value = type(msg).__enter__(mgr) value_id = self.id_factory("value") s = mk_assign(value_id, mk_call_expr(mk_attr(mk_call('type', [mk_name(mgr_id)]), "__enter__"), [mk_name(mgr_id)])) statements.append(s) # exc = True exc_id = self.id_factory("exc") s = mk_assign(exc_id, mk_name("True")) statements.append(s) # try: tryfinally_body = [] tryfinally_finalbody = [] s = ast.TryFinally(body=tryfinally_body, finalbody=tryfinally_finalbody) statements.append(s) # try: tryexcept_body = [] tryexcept_except = [] expt_handler = ast.ExceptHandler(type=None,name=None,body=tryexcept_except) s = ast.TryExcept(body=tryexcept_body, handlers=[expt_handler], orelse=[]) tryfinally_body.append(s) # node.optional_vars = value if node.optional_vars: s = ast.Assign(targets=[node.optional_vars], value=mk_name(value_id)) tryexcept_body.append(s) # body tryexcept_body.extend(node.body) # except: # exc = False s = mk_assign(exc_id, mk_name("False")) tryexcept_except.append(s) # sys.exc_info() sys_exc_info = mk_call_expr(mk_attr(mk_name('sys'), "exc_info"), []) # exit(mgr, *sys.exc_info()) exit_call = mk_call(exit_id, [mk_name(mgr_id)], vararg=sys_exc_info) # not exit(mgr, *sys.exc_info()) test = ast.UnaryOp(op=ast.Not(), operand=exit_call) # if not exit(mgr, *sys.exc_info()): # raise s = ast.If(test=test, body=[ast.Raise(type=None, inst=None, tback=None)], orelse=[]) tryexcept_except.append(s) # finally: # if exc: # exit(mgr, None, None, None) exit_call = mk_call(exit_id, [mk_name(mgr_id), mk_name("None"), mk_name("None"), mk_name("None")]) s = ast.If(test=mk_name(exc_id), body=[ast.Expr(value=exit_call)], orelse=[]) tryfinally_finalbody.append(s) return statements