Skip to content

fix: require cobra>=0.31.1 to avoid deepcopy RecursionError - #35

Merged
edkerk merged 1 commit into
developfrom
fix/cobra-deepcopy-recursion
Aug 27, 2026
Merged

fix: require cobra>=0.31.1 to avoid deepcopy RecursionError#35
edkerk merged 1 commit into
developfrom
fix/cobra-deepcopy-recursion

Conversation

@edkerk

@edkerk edkerk commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

python3 -m pytest -q was failing 146/1330 tests with RecursionError: maximum recursion depth exceeded from inside copy.deepcopy's _reconstruct, across many unrelated test files.

The root cause is not in geckopy's EcModel. A bare two-object cobra.Model (one Reaction, one Metabolite, no geckopy code involved) reproduces the same RecursionError on copy.deepcopy. The actual cause is cobra.Reaction.__copy__/__deepcopy__: they delegate to super() as a trick to avoid infinitely recursing into themselves, which relies on copy memoizing the in-progress object before recursing into its state. Python 3.14's copy module no longer does that for this code path, so the reaction↔metabolite back-reference that exists in any real cobra model (every reaction holds its metabolites; every metabolite holds the reactions it participates in) sends deepcopy into genuine infinite recursion. This is why the failures spanned reaction- and kcat-related tests across the whole suite rather than being confined to EcModel.copy().

This was already fixed upstream: cobra 0.31.0 ("revise the copy mechanisms") deletes the overriding __copy__/__deepcopy__ methods on Reaction entirely, letting the default copy/__getstate__/__setstate__ machinery (which memoizes correctly) handle it. Verified against cobra 0.32.1.

Changes

  • Raise the cobra dependency floor from >=0.29 to >=0.31.1 in pyproject.toml, with a comment explaining why.
  • Add a regression test (test_deepcopy_does_not_recurse_on_reaction_metabolite_cycle in tests/test_ec_model.py) that deep-copies an EcModel with a reaction↔metabolite cycle and checks the copy is independent and correctly rebound. Confirmed it reproduces the RecursionError when the pre-0.31.1 Reaction.__copy__/__deepcopy__ overrides are reinstated.
  • Add a CHANGELOG.md entry under Unreleased / Fixed.

Test plan

  • python3 -m pytest -q --timeout=180 — 1329 passed, 1 deselected, 1 xfailed (was 146 failed before, with cobra <0.31.1)
  • tests/test_ec_model.py — 25 passed, including the two new deepcopy tests
  • Confirmed the new regression test fails with RecursionError when cobra's pre-0.31.1 Reaction.__copy__/__deepcopy__ are reinstated, and passes with them removed (i.e. cobra>=0.31.1)

copy.deepcopy(ec_model) (and of any cobra.Model) raised RecursionError
on Python 3.14. The cause was in cobra, not geckopy: Reaction.__copy__/
__deepcopy__ delegated to super() to avoid recursing into themselves,
relying on copy memoizing an object before recursing into its state --
Python 3.14's copy module no longer does so for that code path. Every
reaction holds its metabolites and every metabolite holds the
reactions it participates in, so the resulting cycle exists in any
real model, full ecModels included.

Fixed upstream in cobra 0.31.0 by deleting those overrides. Bump the
floor to 0.31.1 and add a regression test that deep-copies a model
with a reaction<->metabolite cycle.
@edkerk
edkerk merged commit 51f1226 into develop Aug 27, 2026
5 checks passed
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