Launch plans, schedules, and fixed inputs
Launch plans in flytekit provide a mechanism to parameterize workflow executions, define schedules, and set fixed or default inputs. While every workflow is registered with a default launch plan, you can create custom launch plans to define specific execution configurations without modifying the underlying workflow code.
Creating Launch Plans
The most common way to interact with launch plans is through the LaunchPlan.get_or_create method. If you do not provide a name, flytekit returns the default launch plan for that workflow.
from flytekit import workflow, LaunchPlan
@workflow
def my_wf(a: int, b: str) -> str:
return f"{b}: {a}"
# Get the default launch plan
default_lp = LaunchPlan.get_or_create(workflow=my_wf)
Internally, LaunchPlan.get_or_create manages a cache (LaunchPlan.CACHE) to ensure that multiple calls for the same workflow or named launch plan return the same object, preventing redundant entity creation.
Parameterizing with Default and Fixed Inputs
Launch plans allow you to pre-configure inputs in two ways:
- Default Inputs: Values that can be overridden at execution time.
- Fixed Inputs: Values that are locked and cannot be changed when the launch plan is invoked.
When you define a launch plan with these inputs, flytekit uses transform_inputs_to_parameters and translate_inputs_to_literals to convert Python types into Flyte's internal IDL models.
# Create a named launch plan with specific inputs
custom_lp = LaunchPlan.get_or_create(
name="frequent_execution_lp",
workflow=my_wf,
default_inputs={"a": 10},
fixed_inputs={"b": "fixed-value"}
)
In the LaunchPlan.create method, flytekit ensures that any key present in fixed_inputs is removed from the parameters map (which holds defaults), ensuring that fixed values remain immutable during the launch process.
Scheduling Workflows
Flytekit supports automated execution through schedules. You can attach a schedule to a launch plan using either CronSchedule or FixedRate.
Cron Schedules
CronSchedule supports standard cron expressions or aliases (like @daily or @hourly). It also allows you to pass the scheduled time into the workflow using the kickoff_time_input_arg.
from flytekit import CronSchedule
from datetime import datetime
@workflow
def scheduled_wf(kickoff_time: datetime):
print(f"Running for time: {kickoff_time}")
daily_lp = LaunchPlan.get_or_create(
name="daily_report",
workflow=scheduled_wf,
schedule=CronSchedule(
schedule="0 0 * * *", # Runs every day at midnight
kickoff_time_input_arg="kickoff_time"
)
)
The CronSchedule class validates the expression using croniter. Note that flytekit distinguishes between cron_expression (deprecated AWS-style 6-field cron) and schedule (standard 5-field cron).
Fixed Rate Schedules
FixedRate is used for intervals defined by a timedelta. Flytekit restricts the granularity of these schedules to a minimum of one minute.
from flytekit import FixedRate
from datetime import timedelta
heartbeat_lp = LaunchPlan.get_or_create(
name="heartbeat_lp",
workflow=my_wf,
schedule=FixedRate(duration=timedelta(minutes=10)),
default_inputs={"a": 1, "b": "heartbeat"}
)
The FixedRate class internally translates the timedelta into the largest possible unit (Days, Hours, or Minutes) supported by the Flyte backend via the _translate_duration static method.
Execution and Local Behavior
When you call a LaunchPlan object directly in Python, it behaves differently depending on the context:
- During Compilation: If
ctx.compilation_stateis active, calling the launch plan creates a node in the workflow graph usingcreate_and_link_node. - Local Execution: If called outside of a compilation context, it simply forwards the call to the underlying workflow, merging the
saved_inputs(defaults and fixed values) with any keyword arguments provided at the call site.
# Local execution uses the saved inputs from the launch plan
result = custom_lp(a=20) # 'b' is already fixed as "fixed-value"
Reference Launch Plans
If you need to trigger a launch plan that is already registered on a Flyte cluster from a different project or domain, use ReferenceLaunchPlan. This class acts as a pointer and requires you to define the expected interface manually, as it does not perform a network call to fetch the interface during instantiation.
from flytekit import ReferenceLaunchPlan
ref_lp = ReferenceLaunchPlan(
project="other_project",
domain="development",
name="existing_lp",
version="v1",
inputs={"a": int, "b": str},
outputs={"o0": str}
)