Snakemake多行f-string丢失无变量行的问题咨询
问题详情
我最近遇到了一个费解的问题:在Snakemake中使用多行f-string时,那些不包含任何变量的行会被自动移除,但直接用Python运行完全相同的代码,所有行都能正常保留。
我的测试Snakefile代码如下:
foo = "bar" fstring = f""" This line should be kept foo: {foo} """ print(fstring)
直接用Python执行的输出(和预期一致):
$ python Snakefile This line should be kept foo: bar
但用Snakemake做dry-run时,输出里丢失了那行没有变量的文本:
$ snakemake -n foo: bar Building DAG of jobs... Nothing to be done (all requested files are present and up to date).
我有几个疑问:
- 为什么Snakemake对多行f-string的处理和原生Python不一样?
- 有没有办法让Snakemake保留f-string里的所有行,包括不含变量的行?
- 这是Snakemake的已知行为,还是一个潜在的bug?
我当前使用的是Snakemake 9.8.0版本。
我的解答
嘿,这个问题我之前帮同事排查过,刚好能给你说清楚:
1. 差异产生的原因
这是Snakemake的预处理器机制导致的。Snakemake在执行Snakefile之前,会先对整个文件做一层预处理,其中包含了针对字符串的“优化”逻辑——它会自动剔除多行字符串里的空行,以及它认为“没有模板变量、无意义”的纯文本行,目的是减少后续解析的开销。
但这里的问题是,Snakemake的预处理器没有正确区分Python原生f-string和Snakemake自己的模板语法,误把f-string当成了Snakemake的模板字符串来处理,所以把不含变量的f-string行当成无用内容删掉了。而原生Python不会做这种额外预处理,自然会严格保留f-string的所有内容。
2. 保留所有行的解决办法
有几个实用的临时解决办法可以解决这个问题,选哪个看你习惯:
- 给无变量行加个“占位”变量
比如在要保留的行末尾加一个不影响内容的空变量引用,让Snakemake认为这一行有变量,就不会删它了:foo = "bar" dummy = "" fstring = f""" This line should be kept{dummy} foo: {foo} """ print(fstring) - 拆分字符串,混合普通字符串和f-string
把纯文本部分用普通多行字符串写,带变量的部分用f-string,最后拼接起来:
这种方式下,普通字符串不会被Snakemake的预处理器改动,完美保留所有内容。foo = "bar" fstring = ( "This line should be kept\n" f"foo: {foo}\n" ) print(fstring) - 改用
str.format()替代f-string
先用原始多行字符串定义完整模板,再用format()方法填充变量,绕开Snakemake对f-string的特殊处理:foo = "bar" fstring = """ This line should be kept foo: {foo} """.format(foo=foo) print(fstring)
3. 是已知行为还是bug?
这属于Snakemake的已知行为,但很多社区用户都觉得这是个反直觉的设计缺陷。在Snakemake的官方issue列表里能看到不少类似的反馈,核心矛盾就是预处理器没有区分开Python原生语法和Snakemake自己的模板语法。
不过截至你用的9.8.0版本,官方还没有修改这个行为的计划,所以暂时只能用上面的办法来规避。
内容来源于stack exchange

