def _test_boolean_literal(self, expr: ast.Expression, value: bool) -> None: self.assertIsInstance(expr, ast.Boolean) boolean = cast(ast.Boolean, expr) self.assertEqual(boolean.value, value) # Monkey and Python boolean literals differ if value: self.assertEqual(expr.token_literal(), "true") else: self.assertEqual(expr.token_literal(), "false")
def eval_argument(expr): """ Evaluate argument of workspace method call. """ if not hasattr(expr, "lineno"): setattr(expr, "lineno", 0) return eval(compile(Expression(expr), "<unknown>", 'eval'), context)
def visit_FunctionDef(self, function_def): if function_def.decorator_list and self.IsToDo( function_def.decorator_list[0]): call = function_def.decorator_list[0] function_name = function_def.name if call.args: date = ast.literal_eval(call.args[0]) date = datetime.datetime(*date) else: date = None if date is not None and call.keywords: expr = Expression(call.keywords[0].value) b = compile(expr, '', 'eval') days = eval(b) else: days = None self.todos.append({ 'function_name': function_name, 'date': date, 'days': days, 'lineno': function_def.lineno, })
def matchValue(self, node): # This method could have undesirable side-effects, but conditions in # requirements should not have side-effects to begin with try: code = compile(Expression(node), '<internal>', 'eval') value = eval(code, dict(self.namespace)) except Exception: return None return value
def _compile(e, **kwargs): if kwargs: e = call(lamb(*kwargs.keys())(e), **kwargs) if isinstance(e, Module): mode = "exec" else: mode = "eval" e = Expression(e) fix_missing_locations(e) return compile(e, "<string>", mode)
def eval_code(code_str, context): # Avoid depending on six unless running mathcode directive from six import exec_ mod = parse(code_str, '<string>', 'exec') last_line = mod.body.pop() if isinstance(mod.body[-1], Expr) else None to_exec = compile(mod, '<string>', 'exec') exec_(to_exec, None, context) if last_line is None: return None to_eval = compile(Expression(last_line.value), '<string>', 'eval') return eval(to_eval, None, context)
def matchValue(self, node): """Match any expression which can be evaluated, returning its value. This method could have undesirable side-effects, but conditions in requirements should not have side-effects to begin with. """ try: code = compile(Expression(node), '<internal>', 'eval') value = eval(code, dict(self.namespace)) except Exception: return None return value
def solve(part, s): if part == 1: s = s.replace("*", "-") else: s = s.replace("+", "tmp").replace("*", "+").replace("tmp", "*") module = parse(s) bin_op = module.body[0].value if part == 1: replace_ops_1(bin_op) else: replace_ops_2(bin_op) exe = compile(Expression(body=bin_op), filename="", mode="eval") val = eval(exe) return val
def arts_agenda(func): """ Parse python method as ARTS agenda This decorator can be used to define ARTS agendas using python function syntax. The function should have one arguments which is assumed to be a Workspace instance. All expressions inside the function must be calls to ARTS WSMs. The result is an Agenda object that can be used to copied into a named ARTS agenda Example: >>> @arts_agenda >>> def inversion_iterate_agenda(ws): >>> ws.x2artsStandard() >>> ws.atmfields_checkedCalc() >>> ws.atmgeom_checkedCalc() >>> ws.yCalc() >>> ws.VectorAddVector(ws.yf, ws.y, ws.y_baseline) >>> ws.jacobianAdjustAfterIteration() >>> >>> ws.Copy(ws.inversion_iterate_agenda, inversion_iterate_agenda) """ source = getsource(func) ast = parse(source) func_ast = ast.body[0] if not type(func_ast) == FunctionDef: raise Exception( "ARTS agenda definition can only decorate function definiitons.") args = func_ast.args.args try: arg_name = func_ast.args.args[0].arg except: raise Exception("Agenda definition needs workspace arguments.") ws = Workspace() context = func.__globals__ context[arg_name] == ws # Create agenda a_ptr = arts_api.create_agenda(func.__name__.encode()) agenda = Agenda(a_ptr) for e in func_ast.body: if not type(e.value) == Call: raise Exception("Agendas may only contain call expressions.") # Extract workspace object. try: call = e.value att = call.func.value if not att.id == arg_name: raise (Exception( "Agenda definition may only contain call to WSMs of the " + "workspace argument " + arg_name + ".")) except: raise (Exception( "Agenda definition may only contain call to WSMs of the " + "workspace argument " + arg_name + ".")) # Extract method name. try: name = call.func.attr m = workspace_methods[name] if not type(m) == WorkspaceMethod: raise Exception(name + " is not a known WSM.") except: raise Exception(name + " is not a known WSM.") # Extract positional arguments args = [ws, m] for a in call.args: args.append( eval(compile(Expression(a), "<unknown>", 'eval'), context)) # Extract keyword arguments kwargs = dict() for k in call.keywords: kwargs[k.arg] = eval( compile(Expression(k.value), "<unknown>", 'eval'), context) # Add function to agenda agenda.add_method(*args, **kwargs) return agenda
def arts_agenda(func): """ Parse python method as ARTS agenda This decorator can be used to define ARTS agendas using python function syntax. The function should have one arguments which is assumed to be a Workspace instance. All expressions inside the function must be calls to ARTS WSMs. The result is an Agenda object that can be used to copied into a named ARTS agenda Example: >>> @arts_agenda >>> def inversion_iterate_agenda(ws): >>> ws.x2artsStandard() >>> ws.atmfields_checkedCalc() >>> ws.atmgeom_checkedCalc() >>> ws.yCalc() >>> ws.VectorAddVector(ws.yf, ws.y, ws.y_baseline) >>> ws.jacobianAdjustAfterIteration() >>> >>> ws.Copy(ws.inversion_iterate_agenda, inversion_iterate_agenda) """ source = getsource(func) source = unindent(source) ast = parse(source) func_ast = ast.body[0] if not type(func_ast) == FunctionDef: raise Exception( "ARTS agenda definition can only decorate function definiitons.") args = func_ast.args.args try: arg_name = func_ast.args.args[0].arg except: raise Exception("Agenda definition needs workspace arguments.") ws = Workspace() context = copy(func.__globals__) context.update({arg_name: ws}) # Add resolved non-local variables from closure. nls, _, _, _ = getclosurevars(func) context.update(nls) def eval_argument(expr): if not hasattr(expr, "lineno"): setattr(expr, "lineno", 0) return eval(compile(Expression(expr), "<unknown>", 'eval'), context) # Create agenda a_ptr = arts_api.create_agenda(func.__name__.encode()) agenda = Agenda(a_ptr) illegal_statement_exception = Exception( "Agenda definitions may only contain calls to WSMs of the" "workspace argument " + arg_name + " or INCLUDE statements.") for e in func_ast.body: try: call = e.value except: raise Exception("Agendas may only contain call expressions.") # Include statement if type(call.func) == Name: if not call.func.id == "INCLUDE": raise illegal_statement_exception else: args = [] for a in call.args: args.append(eval_argument(a)) include = Include(*args) arts_api.agenda_append(agenda.ptr, include.agenda.ptr) else: att = call.func.value if not att.id == arg_name: raise illegal_statement_exception # Extract method name. try: name = call.func.attr m = workspace_methods[name] if not type(m) == WorkspaceMethod: raise Exception(name + " is not a known WSM.") except: raise Exception(name + " is not a known WSM.") # Extract positional arguments args = [ws, m] for a in call.args: # Handle starred expression if type(a) == Starred: bs = eval_argument(a.value) for b in bs: args.append(b) continue args.append(eval_argument(a)) # Extract keyword arguments kwargs = dict() for k in call.keywords: kwargs[k.arg] = eval( compile(Expression(k.value), "<unknown>", 'eval'), context) # Add function to agenda agenda.add_method(*args, **kwargs) return agenda
def eval_argument(expr): if not hasattr(expr, "lineno"): setattr(expr, "lineno", 0) return eval(compile(Expression(expr), "<unknown>", 'eval'), context)
def handleExpression(p): return Expression(p[0])
def Disjunction(expr1: Expression, expr2: Expression) -> Expression: """Return expression which is the disjunction of `expr1` and `expr2`.""" expr = Expression(ast.BoolOp(ast.Or(), [expr1.body, expr2.body])) return ast.fix_missing_locations(expr)
def Negation(expr: Expression) -> Expression: """Return expression which is the negation of `expr`.""" expr = Expression(_negate(expr.body)) return ast.fix_missing_locations(expr)
def arts_agenda(func): """ Parse python method as ARTS agenda This decorator can be used to define ARTS agendas using python function syntax. The function should have one arguments which is assumed to be a Workspace instance. All expressions inside the function must be calls to ARTS WSMs. The result is an Agenda object that can be used to copied into a named ARTS agenda Example: >>> @arts_agenda >>> def inversion_iterate_agenda(ws): >>> ws.x2artsStandard() >>> ws.atmfields_checkedCalc() >>> ws.atmgeom_checkedCalc() >>> ws.yCalc() >>> ws.VectorAddVector(ws.yf, ws.y, ws.y_baseline) >>> ws.jacobianAdjustAfterIteration() >>> >>> ws.Copy(ws.inversion_iterate_agenda, inversion_iterate_agenda) """ source = getsource(func) source = unindent(source) ast = parse(source) func_ast = ast.body[0] if not type(func_ast) == FunctionDef: raise Exception("ARTS agenda definition can only decorate function definiitons.") args = func_ast.args.args try: arg_name = func_ast.args.args[0].arg except: raise Exception("Agenda definition needs workspace arguments.") ws = Workspace(0) context = copy(func.__globals__) context.update({arg_name : ws}) # Add resolved non-local variables from closure. nls, _, _, _ = getclosurevars(func) context.update(nls) # # Helper functions # callback_body = [] def callback_make_fun(body): """ Helper function that creates a wrapper function around python code to be executed withing an ARTS agenda. """ m = Module(body) def callback(ptr): try: context[arg_name].ptr = ptr eval(compile(m , "<unknown>", 'exec'), context) except Exception as e: logger.error(r"Exception in Python callback:\n", e) context[arg_name].ptr = None callback_body = [] return callback def eval_argument(expr): """ Evaluate argument of workspace method call. """ if not hasattr(expr, "lineno"): setattr(expr, "lineno", 0) return eval(compile(Expression(expr), "<unknown>", 'eval'), context) # Create agenda a_ptr = arts_api.create_agenda(func.__name__.encode()) agenda = Agenda(a_ptr) illegal_statement_exception = Exception( "Agenda definitions may only contain calls to WSMs of the" "workspace argument " + arg_name + " or INCLUDE statements.") # # Here the body of the function definition is traversed. Cases # that are treated specieal are INCLUDE statements and calls # of workspace methods. Remaining statements are accumulated # in callback_body and then added to the agenda as a single callback. # for e in func_ast.body: if not isinstance(e, Expr): callback_body += [e] continue else: call = e.value if not isinstance(call, Call): callback_body += [e] continue # Include statement if type(call.func) == Name: if not call.func.id == "INCLUDE": callback_body += [e] else: args = [] for a in call.args: args.append(eval_argument(a)) include = Include(*args) if len(callback_body) > 0: agenda.add_callback(callback_make_fun(callback_body)) callback_body = [] arts_api.agenda_append(agenda.ptr, include.agenda.ptr) else: att = call.func.value if not att.id == arg_name: callback_body += [e] continue # Extract method name. name = call.func.attr # m is not a workspace method if not name in workspace_methods: callback_body += [e] continue # m is a workspace method. m = workspace_methods[name] args = [ws, m] for a in call.args: # Handle starred expression if type(a) == Starred: bs = eval_argument(a.value) for b in bs: args.append(b) continue args.append(eval_argument(a)) # Extract keyword arguments kwargs = dict() for k in call.keywords: kwargs[k.arg] = eval( compile(Expression(k.value), "<unknown>", 'eval'), context) # Add function to agenda if len(callback_body) > 0: agenda.add_callback(callback_make_fun(callback_body)) callback_body = [] agenda.add_method(*args, **kwargs) # Check if there's callback code left to add to the agenda. if len(callback_body) > 0: agenda.add_callback(callback_make_fun(callback_body)) callback_body = [] return agenda
def transformAssign(self, node): # Only transform constant assignments if not is_const(node.expr): return self._transform_children(node) # '__foo' names come from each lhs name at the head of the assignment: # 'l = a,b = 0,1' -> 'l = a,b = __a,__b', { '__a':0, '__b':1 } # # Unpack the rhs enough to map each '__foo' name to a value. # 'a,l = 0,[1,2]' -> 'a,l = __a,__l', { '__a':0, '__l':[1,2] } # 'a,l = 2' -> SyntaxError def lvalue_name(node): 'Construct a "magic" name to represent an l-value.' prefix = sep = '__' dot = '_' if isinstance(node, AssName): return prefix + node.name elif isinstance(node, AssAttr): name = node.attrname expr = node.expr while isinstance(expr, Getattr): name = sep.join([expr.attrname, name]) expr = expr.expr if isinstance(expr, Name): expr_name = expr.name else: expr_name = dot return prefix + sep.join([expr_name, name]) # In these trees, strings and tuples are leaves leaves = (str, tuple, AssAttr, AssName) tree_zip = partial(tree.tree_zip, leaves=leaves) flatten = partial(tree.flatten, leaves=leaves) tree_embeds = partial(tree.tree_embeds, leaves=leaves) # Grab the (right-most) lhs and the rhs lhs, rhs = node.nodes[-1], node.expr # Associate constants with l-value names if not tree_embeds(lhs, rhs): raise SyntaxError('Not enough r-values to unpack: %s' % node) zipped = flatten(tree_zip(lhs, rhs)) const_ast_for = map_keys(lambda v: lvalue_name(v), dict(zipped)) # Gather name<->const mappings for names we haven't seen before name_for = {} for name in list(const_ast_for.keys()): if name not in list(self.const_for.keys()): self.const_for[name] = eval_ast(Expression( const_ast_for[name])) assert const_ast_for[name] not in name_for name_for[const_ast_for[name]] = name class C(Transformer): def transform(self, node): if isinstance(node, Node) and node in list(name_for.keys()): return Name(name_for[node]) else: return super(C, self).transform(node) return Assign(node.nodes, C().transform(rhs))
def parse_function(func, allow_callbacks): """ Parse python method as ARTS agenda Args: func: The function object to parse. allow_callbacks: Whether to allow callbacks in the agenda. Return: An 'Agenda' object containing the code in the given function. """ source = getsource(func) source = unindent(source) ast = parse(source) func_ast = ast.body[0] if not type(func_ast) == FunctionDef: raise Exception("ARTS agenda definition can only decorate function definitions.") args = func_ast.args.args try: arg_name = func_ast.args.args[0].arg except: raise Exception("Agenda definition needs workspace arguments.") ws = Workspace(0) context = copy(func.__globals__) context.update({arg_name : ws}) # Add resolved non-local variables from closure. nls, _, _, _ = getclosurevars(func) context.update(nls) # # Helper functions # callback_body = [] def callback_make_fun(body): """ Helper function that creates a wrapper function around python code to be executed withing an ARTS agenda. """ if sys.version_info >= (3, 8): # https://bugs.python.org/issue35894#msg334808 m = Module(body, []) else: m = Module(body) def callback(ptr): try: context[arg_name].ptr = ptr eval(compile(m , "<unknown>", 'exec'), context) except Exception as e: logger.error(r"Exception in Python callback:\n", e) context[arg_name].ptr = None callback_body = [] return callback def eval_argument(expr): """ Evaluate argument of workspace method call. """ if not hasattr(expr, "lineno"): setattr(expr, "lineno", 0) return eval(compile(Expression(expr), "<unknown>", 'eval'), context) # Create agenda a_ptr = arts_api.create_agenda(func.__name__.encode()) agenda = Agenda(a_ptr) illegal_statement_exception = Exception( "Pure ARTS agenda definitions may only contain calls to WSMs of" " the workspace argument '{arg_name}' or INCLUDE statements." " If you want to allow Python callbacks you need to use" " the '@arts_agenda' decorator with the 'allow_callbacks'" " keyword argument set to 'True'." ) # # Here the body of the function definition is traversed. Cases # that are treated specieal are INCLUDE statements and calls # of workspace methods. Remaining statements are accumulated # in callback_body and then added to the agenda as a single callback. # for e in func_ast.body: if not isinstance(e, Expr): if allow_callbacks: callback_body += [e] continue else: raise illegal_statement_exception else: call = e.value if not isinstance(call, Call): if isinstance(call, Str): continue elif allow_callbacks: callback_body += [e] continue else: raise illegal_statement_exception # Include statement if type(call.func) == Name: if call.func.id != "INCLUDE": if allow_callbacks: callback_body += [e] else: raise illegal_statement_exception else: args = [] for a in call.args: args.append(eval_argument(a)) include = Include(*args) if len(callback_body) > 0: agenda.add_callback(callback_make_fun(callback_body)) callback_body = [] arts_api.agenda_append(agenda.ptr, include.agenda.ptr) else: att = call.func.value if not att.id == arg_name: callback_body += [e] continue # Extract method name. name = call.func.attr # m is not a workspace method if name not in workspace_methods: if allow_callbacks: callback_body += [e] continue else: raise ValueError( f"{name} is not a know ARTS WSM." ) # m is a workspace method. m = workspace_methods[name] args = [ws, m] kwargs = dict() for a in call.args: # Handle starred expression if type(a) == Starred: bs = eval_argument(a.value) for b in bs: args.append(b) else: args.append(eval_argument(a)) # Extract keyword arguments for k in call.keywords: if k.arg is None: d = eval(compile(Expression(k.value), "<unknown>", 'eval'), context) kwargs.update(d) else: kwargs[k.arg] = eval(compile(Expression(k.value), "<unknown>", 'eval'), context) # Add function to agenda if len(callback_body) > 0: agenda.add_callback(callback_make_fun(callback_body)) callback_body = [] agenda.add_method(*args, **kwargs) # Check if there's callback code left to add to the agenda. if len(callback_body) > 0: agenda.add_callback(callback_make_fun(callback_body)) callback_body = [] return agenda