<@ULVA73B9P> how can I specify custom names for fl...
# ask-marvin
p
@Marvin how can I specify custom names for flow runs?
m
In Prefect 2.x, you can specify custom names for flow runs when you create a deployment for your flow. A deployment specifies various details regarding how a flow should run, including the entry point to the flow code, infrastructure to run on, schedule, execution parameters, and importantly, the name for the deployment, which will in turn affect the naming of flow runs derived from that deployment. According to the documentation, when you create a Deployment, you have the option to specify how to run your flow, which includes execution details such as parameters and the name of the deployment. Flow run names would typically be derived from the names you give to your deployments. However, the excerpt does not precisely indicate how to set custom flow run names directly at the flow run level instead of the deployment level. If you need to set a custom name for individual flow runs, you would do this programmatically when triggering a flow run. For a more directed answer including a code example, please refer to Prefect's documentation or coding materials, as more information or a clear example was not immediately available in the provided excerpts. For the most accurate and up-to-date instructions, it's recommended to consult the Prefect documentation directly or consider checking the code examples available in the Prefect GitHub repository or forums. If you'd like, I can search the Prefect GitHub repository for issues or discussions related to customizing flow run names. Would you like me to do that?
n
couple different ways are valid, depending how you're invoking it •
run_deployment
accepts a
flow_run_name
kwarg • templating kwargs into a flow e.g.
Copy code
@flow(flow_run_name="Fetch {table}")
def query_table(table: str):
   ...
p
Thanks I dont think we can make it dynamically change going with the second solution right? So talking about the first solution now, is this possible using
create_flow_run
api endpoint?
or we should go with
run_deployment
only?
n
hanks I dont think we can make it dynamically change going with the second solution right?
you mean change the template on a flow-run basis? correct, you cannot
what format do you want for flow run names? or like, what do you plan for the flow run name to be based on
p
we are looking to dynamically allocate some names specific to a project name so we can easily search for status later using flow run name. Project name can change frequently
right now we use
create_flow_run
endpoint and pass headers, payload via a rest post request
It would be ideal if we can pass something like flow_run_name in addition to parameters in the payload
n
Copy code
In [1]: from prefect import flow

In [2]: from prefect.blocks.system import String

In [3]: await String(value="foo-project").save("project-name", overwrite=True)
Out[3]: UUID('18e7bf69-71ac-43af-b1ec-85b42b8345c6')

In [4]: @flow(flow_run_name=f"{String.load('project-name').value}" + " {bar}")
   ...: def foo(bar = 42):
   ...:     pass
   ...:

In [5]: foo()
p
This works if we can grab name from blocks. But what if we want to trigger via rest api and pass the name or even if name is collectable only from foo params
n
this would all happen at flow runtime im just showing that your
flow_run_name
can be dynamically templated with whatever project name you have + parameters passed into the flow run. tldr you dont need to worry about passing the flow run name through the API
you'd just need to update the block value when your project name changes (or make the project name a param of the flow, or other options)
p
Oh I see. Thanks a lot. I will try
How does that formatting you provided really work?
@flow(flow_run_name=f"{String.load('project-name').value}" + " {bar}")
so instead of
bar
we can have whatever expression (even python) and it would be resolved at runtime? It is not exactly a jinja format so curious how it works.
From
String.load('project-name').value
it seems it can be just whatever python
This is not working to be specific
Copy code
@flow(name="flow-name",  flow_run_name=f"{workflow_input[some_key]}")
async def my_flow(workflow_input):
    .........
n
so what i did was a little hacky but it could be made cleaner / more explicit i added together an f-string (populated with value immediately like normal f-string) and a template string
"{some_flow_param}"
that wouldnt be populated with the value of
some_flow_param
until flow runtime (when the value of that param is defined)
so if you inspected the value of what I passed to
flow_run_name
you'd see
Copy code
"foo-project {bar}"
p
It seems prefect doesnt like it when I deploy. I get
Copy code
prefect.exceptions.ScriptError: Script at 'flows/my_flow.py' encountered an exception: NameError("name 'workflow_input' is not defined")
you can think bar instead of workflow_input
n
Copy code
@flow(name="flow-name",  flow_run_name=f"{workflow_input[some_key]}")
async def my_flow(workflow_input):
    .........
this isnt the same thing as what I did
Copy code
f"{workflow_input[some_key]}"
this is all an f-string, where f-strings are going to render immediately, so
workflow_input
needs to be a defined variable at the time you define the string, whereas something like
"{workflow_input}"
is a valid string, which just so happens to be a template string that you could do
.format()
on
in my answer above i assumed workflow_input was a str, even tho it seems yours is a dict, but i dont believe you can have a dict in a template string like that @Marvin can you explain template strings vs f-strings in python, what kind of references can you put in template strings?
m
Template strings and f-strings are both ways to embed expressions inside string literals for formatting in Python: 1. Template Strings: Template strings are available through the
string
module in Python and rely on the
Template
class. They use placeholders defined with
$
and optional curly braces (
{}
), for example:
${variable}
. This method is considered safer for handling user-supplied input, as it avoids directly evaluating the embedded expressions. 2. F-strings: Introduced in Python 3.6 as "formatted string literals," f-strings use curly braces
{}
containing expressions that are replaced with their values. They use an
f
or
F
prefix, like:
f"{expression}"
. F-strings are more concise and generally faster. They can include any valid Python expressions and inline evaluation, including method calls, mathematical operations, and even inline function definitions. What you can put in template strings in Python: - Template strings allow only basic variable substitutions, where the variables are placeholders written within the string with a
$
prefix. - Template strings are more limited compared to f-strings. They don't allow complex expressions or inline evaluation. - You can pass variables or complex expressions to a template string through a dictionary by using the
substitute
method, which matches keys from the dictionary with placeholders in the template. As for the excerpt mentioning a
template
attribute, it seems to refer to a specific context within the Prefect framework, possibly related to the configuration of some resource requirement template. This does not describe Python's general-purpose template strings but likely a Prefect-defined attribute. In the context mentioned in the discussion about not being able to have a dictionary in a template string directly: indeed, in standard Python template strings, you cannot directly embed a dictionary. Instead, you would extract the variables you need or use the
substitute
method to replace placeholders with dictionary values. With f-strings, however, you can directly reference any Python expression, including dictionary lookups, within the string itself.
n
@Marvin sorry by template string i meant like this
Copy code
In [1]: tmpl = "{foo}"

In [2]: tmpl.format(foo="bar")
Out[2]: 'bar'
also be more concise please
m
Sure, in Python, the
format
method allows you to create strings with placeholders enclosed in curly braces
{}
that can be replaced with the values of variables. You can pass these variables as arguments to
format
, and it will substitute the placeholders with the corresponding values based on either positional or keyword arguments. In the example you provided,
"{foo}".format(foo="bar")
,
foo
is a placeholder within the string, and
format(foo="bar")
is replacing that placeholder with the string "bar". The
format
method is quite flexible and allows referencing attributes of objects, indexing, and even accessing items of dictionaries within the placeholders.
p
Got ya. Yeah I was being stupid. so that was a templating format