Task authoring and execution
Flyte tasks are the fundamental building blocks of a workflow. In flytekit, tasks are typically defined by decorating a Python function, which allows the framework to automatically handle type conversion, metadata tracking, and execution environment setup.
Declaring Tasks
The most common way to create a task is using the @task decorator from flytekit.core.task. When you apply this decorator to a function, flytekit inspects the function's type hints and docstrings to construct a PythonFunctionTask.
from flytekit import task
@task
def square(n: int) -> int:
"""
A simple task that squares an integer.
"""
return n * n
Internally, the @task decorator acts as a factory. It creates a TaskMetadata object to store configuration and then instantiates a subclass of PythonFunctionTask. If the decorated function is asynchronous (async def), flytekit automatically uses AsyncPythonFunctionTask instead.
Interface Inference
Flytekit relies on Python type hints to define the task's interface. The transform_function_to_interface function in flytekit.core.interface parses the function signature to create a TypedInterface, which Flyte uses to ensure type safety across different tasks in a workflow.
Task Configuration
You can configure task behavior by passing arguments to the @task decorator. These settings are captured in the TaskMetadata class within flytekit.core.base_task.
Caching and Retries
Caching allows Flyte to skip execution if a task has already been run with the same inputs.
from flytekit import task
@task(cache=True, cache_version="1.0", retries=3)
def fetch_data(query: str) -> str:
...
cache: Enables caching when set toTrue.cache_version: A string that, when changed, invalidates previous cache entries. This is required ifcache=True.retries: The number of times Flyte should attempt to re-run the task upon failure.
Resource Requests and Limits
You can specify the compute resources required for a task using the Resources object.
from flytekit import task, Resources
@task(
requests=Resources(cpu="2", mem="500Mi"),
limits=Resources(cpu="4", mem="1Gi")
)
def memory_intensive_task(data: list) -> int:
...
Core Task Abstractions
Flytekit uses a hierarchical class structure to manage different types of tasks:
Task: The base class inflytekit.core.base_task. it captures the language-agnostic information required by the Flyte IDL, such as thetask_type,name, andinterface.PythonTask: Inherits fromTaskand adds support for Python-native interfaces. It handles the translation between Flyte literals and Python types via theTypeEngine.PythonFunctionTask: The primary class for tasks defined via functions. It implementsexecute()by calling the decorated Python function.
Custom Task Plugins
For specialized execution environments (like Spark or SQL), flytekit provides a plugin system. You can register custom task types using TaskPlugins.register_pythontask_plugin. This allows a specific task_config object to trigger a specialized PythonFunctionTask subclass.
Execution Flow
When a task is executed, flytekit follows a structured 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 or plugin logic.post_execute: Performs cleanup or output modification.- Output Translation: Converts Python return values back into a Flyte
LiteralMapvia_output_to_literal_map.
Local Execution
When you call a task function directly in a Python script, flytekit triggers local_execute. This mode bypasses the Flyte backend and runs the code locally, while still performing type validation and optional local caching via LocalTaskCache.
# Local execution for testing
result = square(n=5)
assert result == 25
Specialized Task Types
Dynamic Tasks
Dynamic tasks allow you to generate a new workflow structure at runtime based on input data. You define these by setting execution_mode=PythonFunctionTask.ExecutionBehavior.DYNAMIC (often via the @dynamic decorator).
Eager Tasks
Eager tasks (EagerAsyncPythonFunctionTask) allow for more flexible, imperative-style execution where Python code acts as the orchestrator, spawning sub-tasks on the Flyte cluster as it runs. This is indicated by is_eager=True in the TaskMetadata.