Skip to content

fix: restore lineqlist2str, which CNApy imports at startup - #76

Merged
VonAlphaBisZulu merged 2 commits into
mainfrom
restore-lineqlist2str
Jul 26, 2026
Merged

fix: restore lineqlist2str, which CNApy imports at startup#76
VonAlphaBisZulu merged 2 commits into
mainfrom
restore-lineqlist2str

Conversation

@VonAlphaBisZulu

Copy link
Copy Markdown
Contributor

The regression

lineqlist2str was removed in 53f98ad (#72) as dead code. It had no caller in this repository or its tests — which is what I checked, and it was the wrong test.

CNApy imports it, at startup:

cnapy/gui_elements/strain_design_dialog.py:12   from straindesign import SDModule, lineqlist2str, ...
cnapy/gui_elements/strain_design_dialog.py:826  text = lineqlist2str(c)
cnapy/gui_elements/main_window.py:41            from cnapy.gui_elements.strain_design_dialog import SDDialog, ...

Because main_window imports that module eagerly, this is an ImportError on CNApy launch, not a broken corner of one dialog.

It is already shipped: 1.19 went to PyPI and the cnapy channel, and CNApy requires straindesign>=1.18 with cnapy as its highest-priority channel — so any fresh CNApy install resolves the broken version.

The fix

Restored verbatim. This was a removal, not a rename: it is distinct from lineqlist2mat (list → matrices) and from linexprdict2str (dict → string), which it calls internally and which still exists.

Blast radius — only this one

All nine names removed in 1.19, checked against CNApy:

Symbol CNApy
lineqlist2str used — breaks
modules_coeff2float, remove_redundant_bounds, lineq2mat, get_rids unused
nullspace alias, compression_backend, compress_model_efmtool, suppressed_reactions unused

CNApy's efmtool/jpype imports come from efmtool_link, its own dependency, and are unaffected by the efmtool removal.

Regression guard

tests/test_13_public_api.py pins the names CNApy imports, plus a behavioural test of lineqlist2str. Grepping this repository cannot see a downstream consumer, so the check has to be written down rather than rediscovered. Verified it fails against current main and passes here.

Also

Quote the version in conda-recipe/meta.yaml. Rendered unquoted, a version like 1.19 is YAML for a floating-point number — conda-build tolerates it, but the conda-forge linter rejects it, and the two recipes shouldn't disagree.

Verified for a 1.19.1 release

Simulated the full workflow at 1.19.1:

Step Result
update_version.py × 3 files rc=0; 1.19.1 in pyproject, meta.yaml, conf.py
python -m build wheel + sdist at 1.19.1
twine check dist/* PASSED both
conda render conda-recipe/ parses; version renders as a string
Install from sdist 1.19.1, lineqlist2str present and correct
PEP 440 ordering 1.19 < 1.19.1

No float-parsing issue: three-component versions are never float-parseable, and the recipe now quotes the value regardless.

🤖 Generated with Claude Code

VonAlphaBisZulu and others added 2 commits July 25, 2026 21:37
lineqlist2str was removed in 53f98ad as dead code. It had no caller in this
repository or its tests, which is what I checked, and that was the wrong test:
CNApy imports it in gui_elements/strain_design_dialog.py, and main_window.py
imports that module at startup, so the removal turns CNApy into an ImportError on
launch rather than a broken corner of one dialog.

1.19 is already published to PyPI and to the cnapy channel, and CNApy requires
straindesign>=1.18 with cnapy as its highest-priority channel, so any fresh
install resolves the broken version. This restores the function verbatim; it was
a removal and not a rename, distinct from lineqlist2mat and from linexprdict2str,
which it calls.

Of the nine names 1.19 removed, this is the only one CNApy uses:
modules_coeff2float, remove_redundant_bounds, lineq2mat, get_rids, the nullspace
alias, compression_backend, compress_model_efmtool and suppressed_reactions are
all unused there. CNApy's efmtool and jpype imports come from efmtool_link, its
own dependency, and are unaffected.

test_13_public_api.py pins the names CNApy imports. Grepping this repository
cannot see a downstream consumer, so the check has to be written down rather than
rediscovered.

Also quote the version in conda-recipe/meta.yaml. Rendered unquoted, a version
like 1.19 is YAML for a floating-point number; conda-build tolerates it but the
conda-forge linter rejects it, and the two recipes should not disagree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hardcoded list of names CNApy imports goes stale the moment CNApy imports
something new, and nothing in this repository can notice: a downstream consumer
is invisible to grep here.

So read CNApy instead of installing it. The test fetches its source, walks the
AST for every `from straindesign ... import`, and asserts each name is still
exported. Installing CNApy was the obvious alternative and is a bad trade: it
depends on Qt and PyQtWebEngine, on cplex, which this matrix already cannot
install on macOS or 3.13, and on jpype, which is the JVM binding removed in #72
and the reason those tests were Windows-only. That would drag all of it back into
twenty jobs to test one import list.

It is marked `downstream` and skipped unless --downstream is passed, following
the existing --medium and --large convention, and the workflow passes the flag
only on the quarterly schedule. It needs network and tracks a branch we do not
control, so it reports drift rather than gating pull requests.

The static list stays. It runs everywhere, offline and unconditionally, and it is
the layer that would have blocked the removal in the first place.

Verified against ffad74b, before the restore: the AST check fails there, as do
both static guards. A test that cannot fail is decoration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@VonAlphaBisZulu
VonAlphaBisZulu merged commit 0f453d5 into main Jul 26, 2026
20 checks passed
@VonAlphaBisZulu
VonAlphaBisZulu deleted the restore-lineqlist2str branch July 26, 2026 01:50
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.

1 participant