diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb6df95..ae4b021 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 -W "error:Could not import matplotlib:UserWarning" -c "import geckopy" diff --git a/CHANGELOG.md b/CHANGELOG.md index b9dc4f6..617eaf4 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. `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/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..0292ef8 100644 --- a/docs/evotune_kcat_tuning.md +++ b/docs/evotune_kcat_tuning.md @@ -41,8 +41,6 @@ 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. - 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..96453fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ 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 @@ -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/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 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 (