Instead of the + operator, I think the df prefix should be the recommended approach when concatenating strings in an indentation-aware manner so that step_instructions when it’s being defined doesn’t have to know in advance how deeply it’s going to be indented when used as a fragment of a larger string:
def write_func(option=1):
func_def = d"""
def func(...):
preinit()
"""
loop_opening = d"""
for i in range{N}:
loopinit()
"""
if option == 1:
step_instructions = d"""
do_stuff_v1(...)
"""
elif option == 2:
step_instructions = d"""
do_stuff_v2(...)
"""
return df"""
{func_def}
{loop_opening}
{step_instructions}
"""
By the way, I think there needs to be some sort of a special rule for df-strings to ignore a newline character if it’s preceded by a {...} that evaluates to a string that ends with a newline, so that we don’t end up with blank lines (double newline characters) between {func_def} and {loop_opening}, and between {loop_opening} and {step_instructions} in the example above.
EDIT: One solution without involving a special rule may be to use line continuation instead:
return df"""
{func_def}\
{loop_opening}\
{step_instructions}\
"""
But this looks a little bit confusing to the reader because when people see a line continuation marker they usually think there isn’t going to be a newline there, when in this case there’s going to be one evaluated from {...}.