Task authoring and execution
Flyte tasks are the fundamental building blocks of workflows in flytekit. They represent a single unit of execution with a strongly typed interface, allowing for independent execution, versioning, and unit testing.
Defining Tasks with the @task Decorator
The primary way to author a task in flytekit is by using the @task decorator from flytekit.core.task. This decorator transforms a standard Python function into a PythonFunctionTask.
from flytekit import task
import typing
@task
def greet(name: str) -> str:
return f"Hello, {name}!"
When you decorate a function with @task, flytekit automatically detects the interface (inputs and outputs) using Python type hints. This information is used to create a TypedInterface that the Flyte backend uses for data validation and orchestration.
Task Configuration
The @task decorator accepts several parameters to control the execution behavior and resource allocation of the task:
retries: The number of times to retry the task on failure.timeout: Adatetime.timedeltaor integer (seconds) representing the maximum duration for a single execution.requests/limits: Define compute resource requirements (CPU, memory, storage) using theResourcesclass.container_image: Specify a custom Docker image for this specific task, overriding the default image.environment: A dictionary of environment variables to be set during execution.cache: Enables caching of results. You can pass a boolean or aCacheobject for advanced configuration.
Example of a configured task:
from datetime import timedelta
from flytekit import task, Resources
@task(
retries=3,
timeout=timedelta(minutes=5),
requests=Resources(cpu="2", mem="500Mi"),
limits=Resources(cpu="4", mem="1Gi"),
cache=True,
cache_version="1.0"
)
def heavy_computation(data: list[int]) -> int:
return sum(data)
Core Task Abstractions
Internally, flytekit uses a hierarchy of classes to manage task behavior.
The Task Base Class
The base_task.Task class is the root of all tasks. It captures the metadata and interface required by the Flyte IDL (Interface Definition Language). It defines the dispatch_execute method, which is the entry point for execution both locally and on the Flyte platform.
PythonTask
base_task.PythonTask extends Task to handle tasks with a Python-native interface. It manages the translation between Flyte's internal Literal types and Python types using the TypeEngine.
PythonFunctionTask
python_function_task.PythonFunctionTask is the implementation used by the @task decorator. It wraps the user's Python function and handles its execution.
Task Execution Flow
When a task is executed, it goes through several stages managed by the Task and PythonTask classes:
pre_execute: Invoked before the task body. This is used to set up execution parameters or modify the context (e.g., initializing a Spark session).- Input Translation: The
dispatch_executemethod calls_literal_map_to_python_inputto convert FlyteLiteralMapinputs into Python-native keyword arguments. execute: The actual user-defined function is called with the translated inputs.post_execute: Invoked after the task body. It can be used for cleanup or to alter the return values.- Output Translation: The
_output_to_literal_mapmethod converts the Python return values back into a FlyteLiteralMap.
Local Execution
When you call a task function directly in a Python script, flytekit triggers local_execute. This bypasses the Flyte backend and runs the code locally, while still performing type validation and (optionally) local caching.
# This triggers local_execute
result = greet(name="Flyte")
Advanced Task Types
Dynamic Tasks
Dynamic tasks are defined using the @dynamic decorator. They allow you to generate a sub-workflow at runtime based on the task's inputs. Internally, this sets the execution_mode of a PythonFunctionTask to ExecutionBehavior.DYNAMIC.
from flytekit import dynamic
@dynamic
def my_dynamic_task(n: int) -> list[str]:
return [greet(name=f"User {i}") for i in range(n)]
Eager Tasks
Eager tasks (using @eager) allow for more flexible, Pythonic execution where task results can be used to decide subsequent task calls within the same execution context. These are implemented via EagerAsyncPythonFunctionTask and require an asynchronous environment.
Reference Tasks
ReferenceTask allows you to point to a task that is already registered on a Flyte cluster without providing the implementation. This is useful for composing workflows across different projects or teams.
from flytekit import reference_task
@reference_task(
project="flytesnacks",
domain="development",
name="core.greet",
version="v1"
)
def remote_greet(name: str) -> str:
...
Task Plugins and Customization
Flytekit supports extensibility through TaskPlugins. By providing a task_config to the @task decorator, you can trigger specialized behavior handled by specific plugin classes (e.g., Spark, SQL, Pod).
The TaskPlugins factory (in flytekit.core.task) maps configuration types to their corresponding PythonFunctionTask implementation. When a task is defined with a specific config, flytekit looks up the registered plugin to handle serialization and execution.