Snakemake中如何定义运行前无法确定类型的规则输出?
Snakemake动态输出场景实现方案
这个场景是checkpoint()的典型适用场景,核心逻辑是:不提前预判输出类型,先执行转换任务,任务完成后自动扫描实际生成的文件列表,再补全整个工作流的依赖DAG,自动适配单文件/多文件两种输出情况。
实现步骤
1. 将转换规则改写为checkpoint
注意原示例中TEI输入路径后缀存在笔误,应为.xml而非.html。checkpoint和普通规则写法基本一致,区别是它执行完成后会触发Snakemake重新评估工作流依赖,不需要提前声明所有不确定的输出:
# 先统一定义路径常量,避免重复硬编码 MANUSCRIPT_BASENAME = "xxx_xx_xxxxx_xxxxx" TEI_INPUT_PATH = f"tei/{MANUSCRIPT_BASENAME}.xml" XHTML_OUTPUT_DIR = "xhtml" checkpoint xhtml_manuscript: input: tei_manuscript = TEI_INPUT_PATH output: # 用一个轻量的完成标记作为checkpoint的输出锚点,标记转换任务执行成功 done = touch(f"{XHTML_OUTPUT_DIR}/{MANUSCRIPT_BASENAME}_convert.done") run: import os # 提前创建输出目录,避免XSLT写入时报错 os.makedirs(XHTML_OUTPUT_DIR, exist_ok=True) shell( f"java -jar {SAXON} -s:{{input.tei_manuscript}} " f"-o:{XHTML_OUTPUT_DIR}/{MANUSCRIPT_BASENAME}.html " f"{TRANSFORMDIR}/other/opt_split_html_sections.xsl" ) # 转换命令执行成功后生成完成标记 shell("touch {output.done}")
2. 编写输出文件聚合函数
这个函数会在checkpoint执行完成后自动运行,扫描输出目录下所有实际生成的、匹配命名规则的xhtml文件,同时覆盖单文件、多拆分文件两种场景:
import glob import os def collect_xhtml_outputs(wildcards): # 阻塞等待checkpoint执行完成 checkpoints.xhtml_manuscript.get() # 匹配两类可能的输出 single_file_candidate = f"{XHTML_OUTPUT_DIR}/{MANUSCRIPT_BASENAME}.html" split_file_candidates = glob.glob(f"{XHTML_OUTPUT_DIR}/{MANUSCRIPT_BASENAME}_sec_*.html") valid_outputs = [] # 单文件存在则加入列表 if os.path.exists(single_file_candidate): valid_outputs.append(single_file_candidate) # 加入所有实际生成的拆分文件 valid_outputs.extend(split_file_candidates) return valid_outputs
3. 关联到默认目标规则
直接将聚合函数作为rule all的输入即可,Snakemake会自动处理执行顺序:先跑转换checkpoint,再拿到实际输出列表,完成整个工作流的依赖构建:
rule all: input: collect_xhtml_outputs
扩展说明
- 如果后续需要批量处理多个手稿,只需将固定的
MANUSCRIPT_BASENAME替换为Snakemake通配符,聚合函数通过wildcards参数获取当前处理的手稿名,扫描对应前缀的输出即可,逻辑不需要大改。 - 该方案天然兼容三类XSLT输出结果:仅生成单文件、仅生成分片文件、同时生成单文件+分片文件,不需要手动加分支判断输出类型。
- 如果后续有其他规则需要处理转换生成的xhtml文件,直接将
collect_xhtml_outputs作为对应规则的输入即可,上下游依赖会自动关联。
内容的提问来源于stack exchange,提问作者Cutú Chiqueño
相关产品推荐
相关产品推荐

