Skip to content

Fix the CI conda cache, the Snakefile f-strings, and blastp calls that fail but exit zero - #108

Closed
mrubash1 wants to merge 3 commits into
Arcadia-Science:mainfrom
mrubash1:mr/ci-cache-and-smoke-test
Closed

Fix the CI conda cache, the Snakefile f-strings, and blastp calls that fail but exit zero#108
mrubash1 wants to merge 3 commits into
Arcadia-Science:mainfrom
mrubash1:mr/ci-cache-and-smoke-test

Conversation

@mrubash1

@mrubash1 mrubash1 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Four small, independent fixes. This replaces #101, which was cut down after #103 landed: the analysis.yml matplotlib and setuptools pins are dropped because #103 took them verbatim, and the python=3.9.16 pins are dropped for the reason below.

The conda env cache restore-keys never matched

The key begins with conda- while the restore-keys begin with snakemake-conda-, so they never matched anything.

Correcting the prefix would be worse than removing them. A restore-keys match still leaves cache-hit false, so the env creation step runs regardless — and that step begins by deleting .snakemake/conda. Making the prefix match would therefore download a stale multi-gigabyte cache only to discard it: strictly slower than the broken prefix that never matched at all.

blastp calls that fail but exit zero

When the remote server refuses to queue a request, blastp -remote reports the error on stderr, writes an empty results file, and still exits zero:

$ blastp -remote -db nr ... ; echo $?
Error: [blastp] bad_request: Could not queue request: DB operation failed.
0

_run_blast_remote printed that stderr but returned the zero exit code unchanged, so the caller treated the failure as a success. The word-size backoff was therefore never attempted — which is the situation it exists for — and the empty results file surfaced later in extract_blast_hits.py as a misleading "no hits were returned".

Failure is now determined by blast_call_failed, which also treats an error on stderr as a failure. An empty results file is deliberately not treated as a failure, because a query that legitimately has no hits produces one too.

This affects the remote backend only; the www backend does not shell out to blastp.

Build the wildcard output paths without f-strings

Nested braces inside an f-string (f"{ANALYSIS_NAME}_{{plotting_mode}}.html") tokenise differently under PEP 701 on Python 3.12+, and snakefmt then rewrites the Snakefile incorrectly. Building the paths without f-strings avoids depending on the interpreter version that happens to run the linter.

On the python pins, and the mamba pin, that are not here

The earlier version of this branch also added python=3.9.16 to analysis.yml, plotting.yml and pandas.yml, and pinned mamba=1.4.2 in cartography_test.yml. Both are dropped, and the reason is worth recording because the first diagnosis was wrong.

The python pins were initially suspected of causing a large regression in env creation time. They were not the cause. The mamba pin was. Pinning mamba=1.4.2 pulls conda 23.3.1 and libmamba 1.4.2 into the outer test environment, and the workflow then runs snakemake --conda-frontend conda against that older conda and its classic solver. Measured on CI:

main this branch with the mamba pin
mamba unpinned, resolves to 2.9.0 1.4.2
analysis.yml creation 35s over 15 minutes, then the runner was reclaimed
whole env creation step 5m10s, succeeded failed

analysis.yml is byte-identical between the two, so the ~25x difference is entirely the solver that the outer environment supplies. The same pin was present on the earlier #101 and #102 branches and explains their env-creation failures too, which had been misattributed to the python pins.

The mamba pin was also fixing a path CI does not use: it exists to make --conda-frontend mamba work, and this workflow deliberately uses --conda-frontend conda. If that is worth solving it wants a different fix -- pinning conda rather than mamba -- and its own PR, with the timing above as the evidence.

The python pins are dropped on their own merits rather than as suspects: CI is green on main without them, since #103's targeted matplotlib and setuptools pins address the actual import failure. If they are still wanted for drift prevention, that is a separate PR.

With no envs/ change left in this branch, the conda cache key now matches main, so this PR restores the cache hit instead of forcing a full env creation.

Testing

ruff and snakefmt clean under CI's pinned versions on Python 3.9; unit tests pass, including four new ones covering the exit-zero case.

mrubash1 and others added 3 commits August 16, 2026 20:17
Two rules built an output path from an f-string that mixed an
interpolated value with an escaped snakemake wildcard:

    f"{ANALYSIS_NAME}_aggregated_features_{{plotting_mode}}.html"

The doubled braces are there so that the f-string renders the literal
`{plotting_mode}` that snakemake needs as a wildcard. That is easy to
misread, and a formatter can silently change what it means: running
`make format` under Python 3.12 or later rewrites the doubled braces to
single ones, which turns the wildcard into an interpolation of a name
that does not exist. (Python 3.12 changed how f-strings are tokenized,
see PEP 701; the versions of snakefmt and black pinned here predate it.)

Concatenating a plain string avoids the escaping entirely, produces a
byte-identical path, and cannot be rewritten this way. Verified that the
DAG still resolves the same targets, with the wildcards expanding to
`..._aggregated_features_pca_umap.html` and
`..._P60709_distribution_analysis.svg` as before, and that
`snakefmt --check` now passes under both Python 3.9 (which CI uses) and
Python 3.12+, where it previously wanted to rewrite the file.

Note that the doubled braces elsewhere in the Snakefile are not affected:
they appear in a plain (non-f) string and in a shell block, neither of
which a formatter rewrites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLpJZQ4W4XsL9NUUjjsry9
The `restore-keys` never matched anything: they begin with "snakemake-conda-" while the
key begins with "conda-". Correcting the prefix would be worse than removing them. A
`restore-keys` match still leaves `cache-hit` false, so the env creation step runs
regardless, and that step begins by deleting `.snakemake/conda`. Making the prefix match
would therefore download a stale multi-gigabyte cache only to discard it -- strictly
slower than the broken prefix that never matched anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLpJZQ4W4XsL9NUUjjsry9
When the remote server refuses to queue a request, `blastp -remote` reports the error on
stderr, writes an empty results file, and still exits zero:

    $ blastp -remote -db nr ... ; echo $?
    Error: [blastp] bad_request: Could not queue request: DB operation failed.
    0

`_run_blast_remote` printed that stderr but returned the zero exit code unchanged, so the
caller treated the failure as a success. The word-size backoff was therefore never
attempted -- which is the situation it exists for -- and the empty results file surfaced
later in `extract_blast_hits.py` as a misleading "no hits were returned".

Failure is now determined by `blast_call_failed`, which also treats an error on stderr as
a failure. An empty results file is deliberately not treated as a failure, because a query
that legitimately has no hits produces one too.

This affects the `remote` backend only; the `www` backend does not shell out to blastp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLpJZQ4W4XsL9NUUjjsry9
@mrubash1
mrubash1 force-pushed the mr/ci-cache-and-smoke-test branch from a884cd4 to ee4f874 Compare August 17, 2026 03:18
@mrubash1 mrubash1 changed the title Fix the CI conda cache, and detect blastp calls that fail but exit zero Fix the CI conda cache, the Snakefile f-strings, and blastp calls that fail but exit zero Aug 17, 2026

@ahmedhosny ahmedhosny left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Three independent, well-justified fixes:

  1. Dropping the conda restore-keys is correct: they never matched (snakemake-conda- vs conda-), and a working prefix would still download a stale cache only for the next step to delete .snakemake/conda.
  2. blast_call_failed treats blastp -remote stderr Error: as a failure even when the process exits 0, which is what unblocks the word-size backoff. An empty results file is correctly not treated as failure. This only affects the remote backend.
  3. Building wildcard output paths without f-strings avoids PEP 701 rewriting the doubled braces under Python 3.12+.

I merged main into this work (conflicts with #107 in blast_utils.py / tests: kept both sides) and opened a follow-up from the resolved branch so it can land on current main.

@ahmedhosny

Copy link
Copy Markdown
Contributor

Thanks — this LGTM.

After merging #107, this branch conflicted in blast_utils.py and tests/test_blast_utils.py (both sides add after the same spots). I merged main in and kept both: the www timeout/error-page work from #107 and the blast_call_failed / f-string / cache-key fixes from here.

Landing the resolved branch as #109 so it can merge onto current main. Will close this once that is in.

@ahmedhosny

Copy link
Copy Markdown
Contributor

Landed on main via #109 (this branch conflicted with #107 after that merge; the follow-up kept both sides). Closing this as superseded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants