Task authoring and execution
Flyte tasks are the fundamental building blocks of a workflow. In flytekit, tasks are defined by decorating a Python function with the @task decorator, which transforms the function into a PythonFunctionTask object. This object captures the function's interface (inputs and outputs), metadata (like retries and timeouts), and execution logic.
Declaring Tasks
The primary way to author a task is using the @task decorator. flytekit uses Python type hints to automatically derive the TypedInterface for the task, ensuring that data types are consistent across the Flyte platform.
from flytekit import task
@task
def add_one(x: int) -> int:
return x + 1
When you apply @task to a function, flytekit performs the following:
- Interface Extraction: It calls
transform_function_to_interfaceto inspect the function signature and create a Flyte-compatible interface. - Task Instantiation: It creates an instance of
PythonFunctionTask(defined inflytekit/core/python_function_task.py). - Plugin Selection: It checks
TaskPluginsto see if a specialized plugin should be used based on thetask_configprovided.
Task Metadata and Configuration
You can configure task behavior by passing arguments to the @task decorator. These arguments are stored in a TaskMetadata object (defined in flytekit/core/base_task.py).
from datetime import timedelta
from flytekit import task, Resources
@task(
retries=3,
timeout=timedelta(minutes=5),
requests=Resources(cpu="1", mem="2Gi"),
limits=Resources(cpu="2", mem="4Gi"),
cache=True,
cache_version="1.0"
)
def resource_intensive_task(data: list[int]) -> int:
return sum(data)
Key configuration options include:
retries: The number of times Flyte should retry the task on failure.timeout: The maximum duration a single execution of the task is allowed to run.cacheandcache_version: Enables caching of task outputs. Ifcache=True, acache_versionmust be provided.requestsandlimits: Specifies the compute resources (CPU, memory, GPU) required for the task using theResourcesclass.
Core Task Abstractions
flytekit uses a class hierarchy to manage different types of tasks:
Task: The base class inflytekit/core/base_task.py. it captures the Flyte IDLTaskTemplateinformation but lacks a Python-native interface.PythonTask: A subclass ofTaskthat adds apython_interface. This is the base for tasks that have a Python-native typed interface.PythonFunctionTask: The most common task type, which wraps a standard Python function. It handles the translation between Flyte literals and Python native types during execution.
Custom Task Types
For tasks that don't fit the standard Python function model (like SQL queries or pre-built container logic), you can inherit from PythonTask. For example, a SQL task might look like this:
from flytekit.core.base_task import PythonTask
from flytekit.core.interface import Interface
class MySqlTask(PythonTask):
def __init__(self, name: str, query: str, **kwargs):
super().__init__(
task_type="sql",
name=name,
interface=Interface(inputs={}, outputs={"result": int}),
**kwargs
)
self._query = query
def execute(self, **kwargs):
# Implementation for executing the SQL query
return 42
Task Execution Flow
When a task is executed, flytekit follows a specific lifecycle managed by the dispatch_execute method in PythonTask:
pre_execute: Prepares the execution environment (e.g., setting up a Spark session).- Input Translation: Converts Flyte
LiteralMapinputs into Python native values using_literal_map_to_python_input. execute: Invokes the actual user code (the decorated function or the overriddenexecutemethod).post_execute: Performs cleanup or output modification.- Output Translation: Converts Python native return values back into a Flyte
LiteralMapusing_output_to_literal_map.
Local vs. Remote Execution
flytekit supports running tasks locally for testing. When you call a task function directly in Python, it triggers local_execute.
# Local execution
result = add_one(x=10)
assert result == 11
In local_execute, flytekit mimics the Flyte platform's behavior by translating inputs to literals and back, ensuring that your type transformers and custom logic work as expected before deploying to a cluster.
Specialized Task Behaviors
Async Tasks
If you decorate an async function with @task, flytekit instantiates an AsyncPythonFunctionTask. This allows you to use await within your task body.
@task
async def async_task(x: int) -> int:
return x + 1
Eager Workflows
Eager workflows (declared with the @eager decorator) allow for dynamic execution where Python code acts as the orchestrator. Unlike standard tasks, eager workflows can await other tasks and make decisions based on their results at runtime.
from flytekit import eager
@eager
async def my_eager_workflow(x: int) -> int:
out = await add_one(x=x)
return await add_one(x=out)
Internally, @eager creates an EagerAsyncPythonFunctionTask which manages a Controller to communicate with the Flyte backend during execution.
Reference Tasks
A reference_task is a pointer to a task that is already registered on a Flyte cluster. It allows you to use tasks defined in other projects or languages without having the source code available locally.
from flytekit import reference_task
@reference_task(
project="flytesnacks",
domain="development",
name="core.control_flow.merge_sort.merge",
version="v1"
)
def merge(sorted_list1: list[int], sorted_list2: list[int]) -> list[int]:
...
The function body of a reference task is ignored; only the signature is used for workflow compilation.