Skip to content

Parse standard operators #223

@liamhuber

Description

@liamhuber

Currently, parseable workflow python is restricted to

  • Assigning variables to names
  • Assigning a list to a variable / appending to it in a for-scope
  • Flow control operations (for/while/if-elif-else/try-except)

Such that we cannot currently parse a workflow like this:

def my_wf_to_parse(a, b, c):
    d = a + b
    e = c + d
    return e

because of the + operators.

However, this is strictly a parsing limitation, and not a workflow limitation. One can get the recipe I imaging we would want from parsing such a definition by manually constructing a node for the operator wrapping the standard library:

import operator

from pyiron_snippets import versions

from flowrep.api import schemas as frs

add_recipe = frs.AtomicNode.model_validate(
    {
        "inputs": ["a", "b"],
        "outputs": ["result"],
        "reference": {
            "info": versions.VersionInfo.of(operator.add),
            "restricted_input_kinds": {
                "a": "POSITIONAL_ONLY",
                "b": "POSITIONAL_ONLY",
            },
        },
    }
)

Since we can use recipes in workflow definitions (#199) and basic atomic recipes are callable (#197), we can write a parseable, recipe-based definition that behaves like our target

import flowrep as fr

@fr.workflow
def my_wf(a, b, c):
    d = add_recipe(a, b)
    e = add_recipe(c, d)
    return e

my_wf(1,2,3)
>>> 6

And is a valid, runnable recipe

from flowrep.api import tools as frt

frt.run_recipe(my_wf.flowrep_recipe, a=1, b=2, c=3).output_ports["e"].value
>>> 6

I.e., getting my_wf_to_parse to produce a valid recipe is purely a parser extension, and based around recognizing that we have an expression using an operator that ought to produce a new node, which we can dynamically produce based on the underlying builtin operator function. The example here is for +, but this pattern holds for all the standard operators.

We'd need to be careful to cross our t's and dot our i's, but from a technical perspective there is nothing particularly difficult here. But, ought we do this? In an off-grid conversation with @samwaseda, he expressed concern that in the current version of the parser, he's not clear what will parse and what won't (I suspect largely because for-loops have introduced this [] list operations). So making this sort of change would be positive in that users could write "more natural" python and have it parse, but negative in that the list of "this is what is parseable" becomes more complex and the boundaries harder to remember. Now, as long as failed parsing raises clean, clear, and helpful error messages when the parsing fails, I lean towards embracing the extra flexibility -- but there is a trade-off.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions