From 1e6db9b24e5295ed6fb126d55960c12d36495fbd Mon Sep 17 00:00:00 2001 From: Victor Lin Date: Wed, 18 Mar 2026 15:49:25 -0700 Subject: [PATCH 1/5] Move comment This only applies to --cores and --resources, not --local-storage-prefix. --- nextstrain/cli/command/build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nextstrain/cli/command/build.py b/nextstrain/cli/command/build.py index 0aa35d7a..b16b9b9d 100644 --- a/nextstrain/cli/command/build.py +++ b/nextstrain/cli/command/build.py @@ -243,12 +243,12 @@ def run(opts): opts.volumes.append(build_volume) # for Docker, Singularity, and AWS Batch - # Automatically pass thru appropriate resource options to Snakemake to - # avoid the user having to repeat themselves (once for us, once for - # snakemake). if opts.exec == "snakemake": snakemake_opts = parse_snakemake_args(opts.extra_exec_args) + # Automatically pass thru appropriate resource options to Snakemake to + # avoid the user having to repeat themselves (once for us, once for + # snakemake). if not snakemake_opts["--cores"]: if opts.cpus: opts.extra_exec_args += ["--cores=%d" % opts.cpus] From 1aa85f40fb54270aab60d55df579dc0885a01247 Mon Sep 17 00:00:00 2001 From: Victor Lin Date: Wed, 18 Mar 2026 15:52:30 -0700 Subject: [PATCH 2/5] Set default Snakemake args in default_exec_args This is more consistent with the run and view commands. --- nextstrain/cli/command/build.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nextstrain/cli/command/build.py b/nextstrain/cli/command/build.py index b16b9b9d..4367f486 100644 --- a/nextstrain/cli/command/build.py +++ b/nextstrain/cli/command/build.py @@ -206,7 +206,7 @@ def register_parser(subparser): nargs = "?") # Register runner flags and arguments - runner.register_runners(parser, exec = ["snakemake", "--printshellcmds", ...]) + runner.register_runners(parser, exec = ["snakemake", ...]) return parser @@ -244,6 +244,11 @@ def run(opts): if opts.exec == "snakemake": + opts.default_exec_args += [ + # Useful to see what's going on; see also 08ffc925. + "--printshellcmds", + ] + snakemake_opts = parse_snakemake_args(opts.extra_exec_args) # Automatically pass thru appropriate resource options to Snakemake to From a9311f1861e4db29805580a2335a73b4cd6dc071 Mon Sep 17 00:00:00 2001 From: Victor Lin Date: Wed, 18 Mar 2026 16:51:28 -0700 Subject: [PATCH 3/5] Add compatibility checks to Conda runner Parallels IMAGE_FEATURE for Docker. --- nextstrain/cli/runner/conda.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/nextstrain/cli/runner/conda.py b/nextstrain/cli/runner/conda.py index 8f41583d..1a8b2843 100644 --- a/nextstrain/cli/runner/conda.py +++ b/nextstrain/cli/runner/conda.py @@ -98,6 +98,7 @@ usable for the platform. """ +from enum import Enum import json import os import platform @@ -173,6 +174,26 @@ } +class ENV_FEATURE(Enum): + pass + + +def env_supports(feature: ENV_FEATURE) -> bool: + """ + Test if the conda environment supports a *feature*, i.e. by version + comparison of the nextstrain-base package against the feature's first + release. + + If the nextstrain-base package is not found, it is assumed to not have + support for the feature. + """ + meta = package_meta(NEXTSTRAIN_BASE) + if not meta: + return False + version = meta.get("version", "0") + return parse_version_lax(version) >= parse_version_lax(feature.value) + + def register_arguments(parser) -> None: """ No-op. No arguments necessary. From df656ab6fc33fb79c7cc6e58c06ce386b1a46fa3 Mon Sep 17 00:00:00 2001 From: Victor Lin Date: Wed, 18 Mar 2026 15:59:22 -0700 Subject: [PATCH 4/5] Run Snakemake with --benchmark-extended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commands used to find compatible versions for Docker¹ and Conda²: curl -s "https://registry.hub.docker.com/v2/repositories/nextstrain/base/tags?page_size=1000" \ | jq -r '.results[].name | select(startswith("build-20250717"))' micromamba search -c nextstrain "nextstrain-base >20250716,<20250718" ¹ https://github.com/nextstrain/docker-base/commit/0d9577d5457edf962666430ba5f8e164b0b9cf0c ² https://github.com/nextstrain/conda-base/commit/0d7a87f7aede2f568121929087b76eecf29fdc76 --- CHANGES.md | 4 ++++ doc/changes.md | 4 ++++ nextstrain/cli/command/build.py | 24 ++++++++++++++++++++++-- nextstrain/cli/command/run.py | 3 +++ nextstrain/cli/runner/conda.py | 3 ++- nextstrain/cli/runner/docker.py | 3 +++ 6 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d96eaead..b6184129 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -24,6 +24,10 @@ development source code and as such may not be routinely kept up to date. ([#501](https://github.com/nextstrain/cli/pull/501)) +* The `build` and `run` commands now run `snakemake` with the + `--benchmark-extended` option for more detailed benchmark files. + ([#467](https://github.com/nextstrain/cli/issues/467)) + # 10.4.2 (7 January 2026) ## Improvements diff --git a/doc/changes.md b/doc/changes.md index 0480819d..4c18eea8 100644 --- a/doc/changes.md +++ b/doc/changes.md @@ -28,6 +28,10 @@ development source code and as such may not be routinely kept up to date. ([#501](https://github.com/nextstrain/cli/pull/501)) +* The `build` and `run` commands now run `snakemake` with the + `--benchmark-extended` option for more detailed benchmark files. + ([#467](https://github.com/nextstrain/cli/issues/467)) + (v10-4-2)= ## 10.4.2 (7 January 2026) diff --git a/nextstrain/cli/command/build.py b/nextstrain/cli/command/build.py index 4367f486..4d402cf5 100644 --- a/nextstrain/cli/command/build.py +++ b/nextstrain/cli/command/build.py @@ -24,8 +24,8 @@ from ..argparse import add_extended_help_flags, AppendOverwriteDefault, SKIP_AUTO_DEFAULT_IN_HELP from ..debug import debug from ..errors import UsageError, UserError -from ..runner import docker, singularity, aws_batch -from ..util import byte_quantity, runner_name, split_image_name, warn +from ..runner import conda, docker, singularity, aws_batch +from ..util import byte_quantity, remove_prefix, runner_name, split_image_name, warn from ..volume import NamedVolume @@ -247,6 +247,10 @@ def run(opts): opts.default_exec_args += [ # Useful to see what's going on; see also 08ffc925. "--printshellcmds", + + # Useful to have additional information in benchmark files. + *(["--benchmark-extended"] + if supports_benchmark_extended(opts) else []), ] snakemake_opts = parse_snakemake_args(opts.extra_exec_args) @@ -313,6 +317,22 @@ def run(opts): return runner.run(opts, working_volume = working_volume, cpus = opts.cpus, memory = opts.memory) +def supports_benchmark_extended(opts) -> bool: + """ + Check if the runner's image or environment supports Snakemake's + ``--benchmark-extended`` option (requires Snakemake ≥8.11.0). + """ + if opts.__runner__ is docker: + return docker.image_supports(docker.IMAGE_FEATURE.benchmark_extended, opts.image) + if opts.__runner__ is aws_batch: + return docker.image_supports(docker.IMAGE_FEATURE.benchmark_extended, opts.image) + if opts.__runner__ is singularity: + return docker.image_supports(docker.IMAGE_FEATURE.benchmark_extended, remove_prefix("docker://", opts.image)) + if opts.__runner__ is conda: + return conda.env_supports(conda.ENV_FEATURE.benchmark_extended) + return False + + def assert_overlay_volumes_support(opts): """ Check that runtime overlays are supported, if given. diff --git a/nextstrain/cli/command/run.py b/nextstrain/cli/command/run.py index 1966a85b..3f0237d1 100644 --- a/nextstrain/cli/command/run.py +++ b/nextstrain/cli/command/run.py @@ -292,6 +292,9 @@ def run(opts): # Useful to see what's going on; see also 08ffc925. "--printshellcmds", + # Useful to have additional information in benchmark files. + "--benchmark-extended", + # In our experience,¹ it's rarely useful to fail on incomplete outputs # (Snakemake's default behaviour) instead of automatically regenerating # them. diff --git a/nextstrain/cli/runner/conda.py b/nextstrain/cli/runner/conda.py index 1a8b2843..19377fa2 100644 --- a/nextstrain/cli/runner/conda.py +++ b/nextstrain/cli/runner/conda.py @@ -175,7 +175,8 @@ class ENV_FEATURE(Enum): - pass + # --benchmark-extended was introduced in Snakemake 8.11.0. + benchmark_extended = "20250717T164942Z" def env_supports(feature: ENV_FEATURE) -> bool: diff --git a/nextstrain/cli/runner/docker.py b/nextstrain/cli/runner/docker.py index ea5182b3..c46eefa1 100644 --- a/nextstrain/cli/runner/docker.py +++ b/nextstrain/cli/runner/docker.py @@ -116,6 +116,9 @@ class IMAGE_FEATURE(Enum): # file overwriting) in ZIP extraction. aws_batch_overlays = "build-20250321T184358Z" + # --benchmark-extended was introduced in Snakemake 8.11.0. + benchmark_extended = "build-20250717T164950Z" + def register_arguments(parser) -> None: # Docker development options From 0fec61fbaa94650a2f67bd46dc7387d539bee23e Mon Sep 17 00:00:00 2001 From: Victor Lin Date: Thu, 19 Mar 2026 10:43:32 -0700 Subject: [PATCH 5/5] Note future alternatives for remove_prefix/suffix --- nextstrain/cli/util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nextstrain/cli/util.py b/nextstrain/cli/util.py index 91ec1537..331bb1bf 100644 --- a/nextstrain/cli/util.py +++ b/nextstrain/cli/util.py @@ -64,6 +64,7 @@ def colored(color, text): ) +# TODO: Use str.removeprefix/removesuffix once Python 3.9 is the minimum supported version. def remove_prefix(prefix, string): return re.sub('^' + re.escape(prefix), '', string)