Hello, a newbie question here ... How much of an ...
# prefect-getting-started
e
Hello, a newbie question here ... How much of an anti-pattern would be to use the task decorator within the flow definition? I'll try to explain myself ... We are trying Prefect -along other frameworks- as orchestrator for a data analysis pipeline. One big advantage we found in Prefect is that, as advertised, it is super easy to convert existing code into a flow managed by Prefect. It is really almost just a matter of "sprinkle in a few decorators, and go". This is a strong advantage over other frameworks, where the code for the flow definition -and sometimes even the tasks definition-, is very much framework-dependent. Using Prefect, you just have your Python code that would otherwise run as any other code, but with the addition of a few decorators, it will be orchestrated by Prefect. Cool! But here's the catch. I said above almost just a matter of "sprinkle in a few decorators, and go", because as soon as you add the task decorator to your functions, they cannot be called anymore as a normal Python function (at least no without changing the code; I mean, I am aware that you can use
task.fn()
to call a task outside of a flow, but that's exactly the point, that by turning a function into a @task, existing code -e.g. tests- that assumes a normal function will not work anymore). One workaround for that is not to use the @task decorator at the function definition, but only within the flow definition. So I illustrate the idea here with a silly but reproducible example: Let's say we have something like this
Copy code
def foo():
    return "foo"

def flow():
    foo()

flow() # runs fine from everywhere, but no benefits from orchestrator
foo() # runs fine to try things out interactively or within a pytest function test_foo
And then we introduce Prefect, pretty easy, just add @task and @flow and there you go
Copy code
from prefect import flow, task

@task
def foo():
    return "foo"

@flow
def flow():
    foo()

flow() # runs fine and you get all the perks of a prefectly orchestrated flow
foo() # but this fails with error "Tasks cannot be run outside of a flow" ¯\_(ツ)_/¯
So my question is how bad an idea is to do something like this:
Copy code
from prefect import flow, task

# DO NOT DECORATE THIS FN AS A TASK
def foo():
    return "foo"

@flow
def flow():
    # BUT ONLY MAKE IT A TASK WITHIN THE FLOW DEFINITION
    foo_task = task(foo)
    foo_task()

flow() # runs fine
foo() # again, runs fin everywhere
That works, and you can keep using your little foo function everywhere. But not sure if such a thing is an anti-pattern that could come back to bite us later down the line.
👍 1
n
i wouldn’t call this an anti pattern! i do this often when i have a function that’s useful in its own right, that i may or may not want to act as a task (or even more fun, if i want to load a function from an import string in a flow and then run that as a task)
i will also say that
with_options
is also a useful class method on tasks and flows that allow you to configure existing task/flow objects dynamically that often circumvents the need to do something like
task(**options)(my_fn)
e
Thanks Nate, that's good to hear. To me it seems perfectly fine as well. But since this is our first experience with prefect and I am not familiar with its internals and potential intricacies, I was wondering if that could lead to issues down the line. Also, I don't remember having seen that kind of thing in any of the examples / tutorials.
I was actually wondering about that design decision. I guess there might be some reasons why they decided to prevent a task-decorated function to run outside a flow. I mean, just as it detects that it's not running within a flow to trigger an error, they could have decided to simply let the original function run without prefect's bells and whistles (and perhaps just a warning or info message). For our use case, that would be much more convenient.
But then again, that's perhaps just that I do not fully grasp the whole system and it's complexities yet, that might as well explain such a design decision.
n
we have something in the works as far as allowing tasks outside of flows :) but for now that’s a hard requirement (that they must be called inside a flow) there’s no direct downside (besides maybe readability?) associated with avoiding the decorator syntax sugar and wrapping functions dynamically
👍 1
e
That's great to hear. We'll keep exploring prefect and how to make the best use of it. Meanwhile just many thanks for a nicely crafted piece of software.👏
n
catjam