Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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