Excellent! Thank you.
This is the short version of what I think happens next:
During Flow.run(...), Flow._run(...) creates a while not flow_state.is_finished() loop of the following. The Flow has a FlowRunner (eventually) run all the Tasks with FlowRunner.run(...). This eventually calls the Executor to start with a executor.start(...) context manager. The FlowRunner then loops once through each Task in order using FlowRunner.flow.sorted_tasks(), doing the following. For each Task, the Executor submits the Task with the run_task function using executor.submit(run_task, ...). This starts the TaskRunner, which eventually has the Executor run the task with prefect.utilities.executors.run_task_with_timeout(...) that eventually calls task.run(...), which is the thing I defined by putting the @task decorator over a function definition in my original .py file.
First, does that seem right?
Second, does the loop for self.flow.sorted_tasks() just kick off a Task in the Executor one after another (returning state = Running or something), or does it wait for each one to finish? If it only starts the Task, is this the purpose of the loop waiting on flow_state.is_finished() -- to just keep looping until all the Tasks are finished?