From 0c7133c02e1f099541b9d022782d9b7ad0538ac0 Mon Sep 17 00:00:00 2001 From: Eduard Kerkhoven Date: Fri, 6 Nov 2026 12:59:03 +0100 Subject: [PATCH 1/3] Require cma cma is a dependency of geckopy and the extra evotune is removed. import geckopy raised ModuleNotFoundError without the extra, because review_assignment imports a module of the evotune subpackage. A CI job installs geckopy without extras and imports it. --- .github/workflows/test.yml | 17 ++++++++++++++- CHANGELOG.md | 1 + README.md | 2 +- docs/evotune_kcat_tuning.md | 3 +-- pyproject.toml | 4 +--- src/geckopy/kcat_tuning/__init__.py | 24 +-------------------- src/geckopy/kcat_tuning/evotune/__init__.py | 7 ------ tutorials/full_ecModel/protocol.py | 3 +-- 8 files changed, 22 insertions(+), 39 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb6df95..c79d63e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,22 @@ jobs: if: matrix.python-version == '3.15' run: sudo apt-get update && sudo apt-get install -y libglpk-dev swig - name: Install - run: pip install -e ".[dev,evotune]" + run: pip install -e ".[dev]" - name: Run pytest run: pytest -q + + import-without-extras: + name: import without the optional extras + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml + - name: Install without extras + run: pip install -e . + - name: Import geckopy + run: python -c "import geckopy, geckopy.kcat_tuning.evotune" diff --git a/CHANGELOG.md b/CHANGELOG.md index b9dc4f6..cc4a738 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* `cma` is a required dependency of geckopy and the extra `evotune` is removed (was: `import geckopy` raised `ModuleNotFoundError: No module named 'cma'` without the extra since 4.0.0b2, because `review_assignment` imports a module of the `evotune` subpackage). `pip install geckopy[evotune]` still installs geckopy and `pip` prints a warning that the extra does not exist. * `enzyme_usage` reports `measured_protein`, `demanded_protein` and `demand_ratio`, the usage of the enzymes with a measured concentration divided by their measured concentrations, and the same restricted to enzymes with a usage above 1e-7 mg/gDCW (`measured_protein_active`, `demand_ratio_active`). `report_enzyme_usage` carries `demand_ratio` and `demand_ratio_active`. GECKO's `getEnzymeUsage` and `reportEnzymeUsage` give the same values. * geckopy is licensed under the GNU General Public License v3 (GPL-3.0), as RAVEN and raven-toolbox. `LICENSE`, the `license` field and the classifier of `pyproject.toml`, and the README state it. Releases up to 4.0.0b2 were published under the MIT licence. * `load_prot_data` takes `zeros_as_measured`: by default an abundance of 0 is read as not measured (NaN) before any filter, also with `filter_data=False`, so the mean of a protein with a zero replicate leaves the zeros out; with `True` a 0 counts as a measured replicate, is kept in the mean and is exempt from `min_val` and `cut_lowest`, as GECKO's `loadProtData` option `zerosAsMeasured`. diff --git a/README.md b/README.md index ac07062..7f05af0 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Both geckopy and raven-toolbox are pre-releases, so pip needs `--pre`: pip install --pre geckopy ``` -Extras: `geckopy[tutorial]` (matplotlib), `geckopy[evotune]` (cma, for kcat tuning) and `geckopy[dev]` (pytest and ruff). Python 3.11 or newer. +Extras: `geckopy[tutorial]` (matplotlib), `geckopy[dev]` (pytest and ruff). Python 3.11 or newer. ## Quick start diff --git a/docs/evotune_kcat_tuning.md b/docs/evotune_kcat_tuning.md index e03fedc..f358d9f 100644 --- a/docs/evotune_kcat_tuning.md +++ b/docs/evotune_kcat_tuning.md @@ -41,8 +41,7 @@ least-trusted sources, has. - A working ecModel and `ModelAdapter` project (an existing `model_adapter.toml` and `models/*.yml`). -- `pip install geckopy[evotune]` -- the CMA-ES search - (`cma`) is an optional dependency, not part of the base install. +- `cma` -- the CMA-ES search; `pip install geckopy` installs it. - Experimental data: at least one of a set of measured exchange fluxes per condition, or a set of measured maximum growth rates per carbon source. Both together works and is preferred -- flux data pins the diff --git a/pyproject.toml b/pyproject.toml index e31452d..9d7a8de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ dependencies = [ "ruamel.yaml>=0.17", "scipy>=1.10", "tqdm>=4.65", + "cma>=3.3", ] [project.optional-dependencies] @@ -68,9 +69,6 @@ dev = [ tutorial = [ "matplotlib>=3.5", ] -evotune = [ - "cma>=3.3", -] [project.urls] Homepage = "https://github.com/CompMetEng/geckopy" diff --git a/src/geckopy/kcat_tuning/__init__.py b/src/geckopy/kcat_tuning/__init__.py index 59eac2e..71e9c33 100644 --- a/src/geckopy/kcat_tuning/__init__.py +++ b/src/geckopy/kcat_tuning/__init__.py @@ -14,29 +14,7 @@ from .sigma_fitter import SigmaFitterResult, fit_sigma, sigma_fitter from .truncate_values import truncate_values -try: - from . import evotune -except ImportError as exc: # pragma: no cover - exercised - # only when the optional `cma` dependency is absent. - # Bound to a module-level name: Python clears an `except ... as` - # target when the block exits, so the class below cannot close over - # `exc` itself. - _evotune_import_error = exc - - class _MissingEvotune: - """Stand-in for the `evotune` subpackage when its - `cma` dependency isn't installed. Raises only on first attribute - access, not on import, so plain `geckopy.kcat_tuning` - stays usable without the optional extra.""" - - def __getattr__(self, name: str): - raise ImportError( - "Evotune: CMA-ES kcat tuning requires the optional " - "'evotune' extra: pip install " - "geckopy[evotune]" - ) from _evotune_import_error - - evotune = _MissingEvotune() # type: ignore[assignment] +from . import evotune __all__ = [ "Correction", diff --git a/src/geckopy/kcat_tuning/evotune/__init__.py b/src/geckopy/kcat_tuning/evotune/__init__.py index 7243d87..abb3672 100644 --- a/src/geckopy/kcat_tuning/evotune/__init__.py +++ b/src/geckopy/kcat_tuning/evotune/__init__.py @@ -4,13 +4,6 @@ :func:`~.tuning.screen_kcat_leverage`, then :func:`~.tuning.select_tunable_mask`, then :func:`~.tuning.cmaes_kcat_tuning`. - -Modules in this package that need ``cma`` import it unconditionally -- -it's a hard requirement here, gated one level up instead (see -``kcat_tuning/__init__.py``) so importing plain -``geckopy.kcat_tuning`` never requires the optional -``evotune`` extra. Install it with -``pip install geckopy[evotune]``. """ from .data import EvotuneData, load_evotune_data from .distance import ( diff --git a/tutorials/full_ecModel/protocol.py b/tutorials/full_ecModel/protocol.py index f673bc0..6ee2645 100644 --- a/tutorials/full_ecModel/protocol.py +++ b/tutorials/full_ecModel/protocol.py @@ -270,8 +270,7 @@ # %% [markdown] # Besides step-wise sensitivity tuning, kcats can be fitted to # experimental growth rates and exchange fluxes with CMA-ES -# (`geckopy.kcat_tuning.evotune`, installed with the `evotune` extra; -# see docs/evotune_kcat_tuning.md): +# (`geckopy.kcat_tuning.evotune`; see docs/evotune_kcat_tuning.md): # # ```python # from geckopy.kcat_tuning.evotune import ( From a7d31db4e8c6be86490cfed150f05d95d95e5a86 Mon Sep 17 00:00:00 2001 From: Eduard Kerkhoven Date: Fri, 6 Nov 2026 14:24:57 +0100 Subject: [PATCH 2/3] Filter the matplotlib warning of cma and tidy the dependency change Importing cma without matplotlib no longer warns on import geckopy, and the CI job imports geckopy with that warning as an error. cma stands in alphabetical order in the dependencies, and the changelog and the evotune guide state the current dependency. --- .github/workflows/test.yml | 2 +- CHANGELOG.md | 2 +- docs/evotune_kcat_tuning.md | 1 - pyproject.toml | 2 +- src/geckopy/kcat_tuning/evotune/tuning.py | 7 ++++++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c79d63e..ae4b021 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,4 +59,4 @@ jobs: - name: Install without extras run: pip install -e . - name: Import geckopy - run: python -c "import geckopy, geckopy.kcat_tuning.evotune" + run: python -W "error:Could not import matplotlib:UserWarning" -c "import geckopy" diff --git a/CHANGELOG.md b/CHANGELOG.md index cc4a738..23ec71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* `cma` is a required dependency of geckopy and the extra `evotune` is removed (was: `import geckopy` raised `ModuleNotFoundError: No module named 'cma'` without the extra since 4.0.0b2, because `review_assignment` imports a module of the `evotune` subpackage). `pip install geckopy[evotune]` still installs geckopy and `pip` prints a warning that the extra does not exist. +* `cma` is a required dependency of geckopy and the extra `evotune` is removed. `import geckopy` imports `cma` through `geckopy.gather_kcats.review` and `geckopy.kcat_tuning.evotune`. `pip install geckopy[evotune]` installs geckopy, and pip prints a warning that the extra does not exist. * `enzyme_usage` reports `measured_protein`, `demanded_protein` and `demand_ratio`, the usage of the enzymes with a measured concentration divided by their measured concentrations, and the same restricted to enzymes with a usage above 1e-7 mg/gDCW (`measured_protein_active`, `demand_ratio_active`). `report_enzyme_usage` carries `demand_ratio` and `demand_ratio_active`. GECKO's `getEnzymeUsage` and `reportEnzymeUsage` give the same values. * geckopy is licensed under the GNU General Public License v3 (GPL-3.0), as RAVEN and raven-toolbox. `LICENSE`, the `license` field and the classifier of `pyproject.toml`, and the README state it. Releases up to 4.0.0b2 were published under the MIT licence. * `load_prot_data` takes `zeros_as_measured`: by default an abundance of 0 is read as not measured (NaN) before any filter, also with `filter_data=False`, so the mean of a protein with a zero replicate leaves the zeros out; with `True` a 0 counts as a measured replicate, is kept in the mean and is exempt from `min_val` and `cut_lowest`, as GECKO's `loadProtData` option `zerosAsMeasured`. diff --git a/docs/evotune_kcat_tuning.md b/docs/evotune_kcat_tuning.md index f358d9f..0292ef8 100644 --- a/docs/evotune_kcat_tuning.md +++ b/docs/evotune_kcat_tuning.md @@ -41,7 +41,6 @@ least-trusted sources, has. - A working ecModel and `ModelAdapter` project (an existing `model_adapter.toml` and `models/*.yml`). -- `cma` -- the CMA-ES search; `pip install geckopy` installs it. - Experimental data: at least one of a set of measured exchange fluxes per condition, or a set of measured maximum growth rates per carbon source. Both together works and is preferred -- flux data pins the diff --git a/pyproject.toml b/pyproject.toml index 9d7a8de..fedc9e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ # override that recurses infinitely (RecursionError) on Python 3.14 for # any model with a reaction<->metabolite reference cycle -- i.e. any real # cobra model. Fixed upstream by deleting that override. + "cma>=3.3", "cobra>=0.31.1", "numpy>=1.21", "pandas>=2", @@ -57,7 +58,6 @@ dependencies = [ "ruamel.yaml>=0.17", "scipy>=1.10", "tqdm>=4.65", - "cma>=3.3", ] [project.optional-dependencies] diff --git a/src/geckopy/kcat_tuning/evotune/tuning.py b/src/geckopy/kcat_tuning/evotune/tuning.py index 9585e68..3d68b09 100644 --- a/src/geckopy/kcat_tuning/evotune/tuning.py +++ b/src/geckopy/kcat_tuning/evotune/tuning.py @@ -57,12 +57,12 @@ from __future__ import annotations import logging +import warnings from contextlib import nullcontext from dataclasses import dataclass, field from itertools import combinations from typing import TYPE_CHECKING, Callable, Optional, Sequence -import cma import cobra import numpy as np import pandas as pd @@ -77,6 +77,11 @@ from .simulate import simulate_evotune_dataset from .tying import isozyme_tie_map +with warnings.catch_warnings(): + # cma warns when matplotlib is absent; the plotting functions of cma are not used here. + warnings.filterwarnings("ignore", message="Could not import matplotlib", category=UserWarning) + import cma + if TYPE_CHECKING: from ...adapter import ModelAdapter from ...adapter.params import EvotuneParams From 6ee263324bb6b243597b756b9574ca6e846bb8ed Mon Sep 17 00:00:00 2001 From: Eduard Kerkhoven Date: Fri, 6 Nov 2026 14:29:30 +0100 Subject: [PATCH 3/3] Place cma first in the dependencies and shorten the changelog entry --- CHANGELOG.md | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23ec71d..617eaf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* `cma` is a required dependency of geckopy and the extra `evotune` is removed. `import geckopy` imports `cma` through `geckopy.gather_kcats.review` and `geckopy.kcat_tuning.evotune`. `pip install geckopy[evotune]` installs geckopy, and pip prints a warning that the extra does not exist. +* `cma` is a required dependency of geckopy and the extra `evotune` is removed. `import geckopy` imports `cma`. * `enzyme_usage` reports `measured_protein`, `demanded_protein` and `demand_ratio`, the usage of the enzymes with a measured concentration divided by their measured concentrations, and the same restricted to enzymes with a usage above 1e-7 mg/gDCW (`measured_protein_active`, `demand_ratio_active`). `report_enzyme_usage` carries `demand_ratio` and `demand_ratio_active`. GECKO's `getEnzymeUsage` and `reportEnzymeUsage` give the same values. * geckopy is licensed under the GNU General Public License v3 (GPL-3.0), as RAVEN and raven-toolbox. `LICENSE`, the `license` field and the classifier of `pyproject.toml`, and the README state it. Releases up to 4.0.0b2 were published under the MIT licence. * `load_prot_data` takes `zeros_as_measured`: by default an abundance of 0 is read as not measured (NaN) before any filter, also with `filter_data=False`, so the mean of a protein with a zero replicate leaves the zeros out; with `True` a 0 counts as a measured replicate, is kept in the mean and is exempt from `min_val` and `cut_lowest`, as GECKO's `loadProtData` option `zerosAsMeasured`. diff --git a/pyproject.toml b/pyproject.toml index fedc9e0..96453fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,11 +35,11 @@ classifiers = [ "Topic :: Scientific/Engineering :: Bio-Informatics", ] dependencies = [ + "cma>=3.3", # >=0.31.1: earlier cobra versions carry a Reaction.__copy__/__deepcopy__ # override that recurses infinitely (RecursionError) on Python 3.14 for # any model with a reaction<->metabolite reference cycle -- i.e. any real # cobra model. Fixed upstream by deleting that override. - "cma>=3.3", "cobra>=0.31.1", "numpy>=1.21", "pandas>=2",