Task authoring and execution
Flyte tasks are the fundamental building blocks of flytekit workflows. They represent a unit of execution with a strongly typed interface, allowing for versioning, independent execution, and unit testing.
Declaring Tasks with the @task Decorator
The most common way to define 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
@task
def greet(name: str) -> str:
return f"Hello, {name}!"
When you apply @task, flytekit uses transform_function_to_interface to automatically infer the task's input and output types from the function's type hints. This ensures that the task adheres to Flyte's type system.
Task Configuration and Metadata
The @task decorator accepts several parameters to control execution behavior, resource allocation, and caching. These parameters are encapsulated internally within the TaskMetadata class in flytekit.core.base_task.
- Retries: Use
retriesto specify how many times the task should be re-run on failure. - Timeouts: Use
timeout(as anintseconds ordatetime.timedelta) to limit the execution duration. - Caching: Enable caching with
cache=Trueand provide acache_version. Flyte will skip execution if the same inputs and version are encountered again. - Resources: Specify
requestsandlimitsusing theResourcesclass to define CPU, memory, and GPU requirements.
from datetime import timedelta
from flytekit import task, Resources
@task(
retries=3,
timeout=timedelta(minutes=5),
cache=True,
cache_version="1.0",
requests=Resources(cpu="2", mem="500Mi"),
limits=Resources(cpu="4", mem="1Gi")
)
def resource_intensive_task(data: list[int]) -> int:
return sum(data)
Core Task Abstractions
Flytekit provides a hierarchy of classes to handle different task types and execution modes.
Task and PythonTask
The Task class in flytekit.core.base_task is the base for all tasks. It maps to the Flyte IDL TaskTemplate. PythonTask extends this to support Python-native interfaces, handling the translation between Python types and Flyte literals via the TypeEngine.
PythonFunctionTask
Located in flytekit.core.python_function_task, this class is the engine behind the @task decorator. It captures the user's function and manages its execution.
execute: This method is called during local execution or by the Flyte executor. It simply invokes the underlying_task_function.dispatch_execute: This is the entry point used by the Flyte platform at runtime. It handles input translation from Flyte literals to Python types, callsexecute, and then translates the results back to literals.
PythonInstanceTask
For tasks that do not have a user-defined function body but instead have a platform-defined execute method, you should inherit from PythonInstanceTask. This is useful for building reusable task plugins where the logic is encapsulated within the class itself.
Task Execution Flow
When a task is executed, flytekit manages the transition between the Flyte type system and Python native code.
- Input Translation: In
dispatch_execute, theinput_literal_map(Flyte's internal representation) is converted to Python nativekwargsusing_literal_map_to_python_input. - Pre-execution: The
pre_executemethod is called, allowing for context setup (e.g., initializing a Spark session). - User Code Execution: The
executemethod runs the actual logic. - Post-execution: The
post_executemethod allows for cleanup or output modification. - Output Translation: The Python return values are converted back into a
LiteralMapvia_output_to_literal_map.
Local Execution
You can run tasks locally just like regular Python functions. Flytekit's local_execute method handles this by mocking the Flyte environment. If caching is enabled locally, LocalTaskCache (found in flytekit.core.base_task) will attempt to retrieve results from a local cache before executing the task.
Task Resolvers
When a task runs on a remote Flyte cluster, the container needs to know how to find and load the specific task code. This is handled by TaskResolverMixin.
The default_task_resolver in flytekit.core.python_auto_container is the standard implementation. It works by:
loader_args: Identifying the module and function name of the task during serialization.load_task: Importing the module and retrieving the task object at runtime usingimportlib.
If you need custom loading logic (e.g., loading tasks from a database or a dynamic source), you can implement a custom resolver by inheriting from TaskResolverMixin and passing it to the @task decorator.
Special Task Types
Async Tasks
If you define a task as an async def, flytekit automatically uses AsyncPythonFunctionTask. These tasks are executed within an event loop managed by flytekit's loop_manager.
Eager Tasks
Eager tasks (declared via EagerAsyncPythonFunctionTask) allow for more dynamic execution patterns where Python code acts as the orchestrator, spawning other Flyte executions and awaiting their results. This is indicated by setting is_eager=True in the TaskMetadata.
Reference Tasks
A ReferenceTask is a pointer to a task that already exists on a Flyte cluster. It allows you to call tasks defined in other projects or domains 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]:
...