diff --git a/.github/update_version.py b/.github/update_version.py index 0b04530..42179f1 100644 --- a/.github/update_version.py +++ b/.github/update_version.py @@ -1,13 +1,47 @@ +"""Set the version number in one of the files that declare it. + +Usage: python update_version.py + +Each pattern below targets exactly one declaration style. A file that matches +none of them is treated as an error rather than rewritten unchanged, so a +renamed key or a reformatted file fails the release run instead of shipping a +stale version number. +""" import re import sys -file_i = sys.argv[1] -version = sys.argv[2] +# (pattern, flags) -- group 1 is the prefix up to the opening quote, group 2 the +# closing quote, so the version itself is whatever sits between them. +PATTERNS = ( + # pyproject.toml version = "1.19" + (r'^(version\s*=\s*")[^"]*(")', re.M), + # docs/source/conf.py release = "1.19" + (r'^(release\s*=\s*")[^"]*(")', re.M), + # conda-recipe/meta.yaml {% set version = '1.19' %} + (r"(\{%\s*set\s+version\s*=\s*')[^']*(')", 0), +) + + +def main(): + if len(sys.argv) != 3: + sys.exit(__doc__) + path, version = sys.argv[1], sys.argv[2] + + with open(path, 'r') as f: + content = f.read() + + total = 0 + for pattern, flags in PATTERNS: + content, count = re.subn(pattern, r'\g<1>' + version + r'\g<2>', content, flags=flags) + total += count + + if not total: + sys.exit(f'{path}: no version declaration matched, version NOT updated') + + with open(path, 'w') as f: + f.write(content) + print(f'{path}: set version to {version} ({total} occurrence(s))') + -with open(file_i, 'r') as f: - content = f.read() - content_new = re.sub(r"(?<=version\=\").*?(?=\")", str(version), content, flags=re.M) - content_new = re.sub(r"(?<=version \= ').*?(?=')", str(version), content_new, flags=re.M) - content_new = re.sub(r"(?<=release \= \").*?(?=\")", str(version), content_new, flags=re.M) -with open(file_i, 'w') as f: - f.write(content_new) +if __name__ == '__main__': + main() diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 02ea77d..2cf9fdc 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -29,7 +29,7 @@ jobs: python-version: '3.x' - name: update version number run: | - python ./.github/update_version.py setup.py ${{ github.event.inputs.version }} + python ./.github/update_version.py pyproject.toml ${{ github.event.inputs.version }} python ./.github/update_version.py conda-recipe/meta.yaml ${{ github.event.inputs.version }} python ./.github/update_version.py docs/source/conf.py ${{ github.event.inputs.version }} - name: install yapf @@ -59,14 +59,17 @@ jobs: gh release create "v${{ github.event.inputs.version }}" \ --title "v${{ github.event.inputs.version }}" \ --notes "${{ github.event.inputs.description }}" - - name: Install setuptools, wheel and twine + - name: Install build and twine run: | - pip install build setuptools wheel twine + pip install --upgrade build twine - name: Clean PyPi build directories run: rm -rf dist - name: Build for PyPi run: | - python setup.py sdist bdist_wheel + python -m build + - name: Check the built distributions + run: | + twine check dist/* - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/README.md b/README.md index b8ba489..a05beeb 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Documentation Status](https://readthedocs.org/projects/straindesign/badge/?version=latest)](https://readthedocs.org/projects/straindesign/builds/) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/straindesign.svg)](https://pypi.org/project/straindesign/) [![CI-test Status](https://github.com/klamt-lab/straindesign/workflows/CI-test/badge.svg)](https://github.com/klamt-lab/straindesign/actions/workflows/CI-test.yml) -[![License](https://img.shields.io/pypi/l/straindesign.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html) +[![License](https://img.shields.io/pypi/l/straindesign.svg)](https://github.com/klamt-lab/straindesign/blob/main/LICENSE) [![Code style: YAPF](https://img.shields.io/badge/code%20style-yapf-blue)](https://github.com/google/yapf) ## A COBRApy[1]-based package for computational design of metabolic networks diff --git a/Update_version.md b/Update_version.md index 1cd75be..d47ec37 100644 --- a/Update_version.md +++ b/Update_version.md @@ -1,33 +1,42 @@ -This is only needed for manual version upgrading. A versioning script is availabe in GitHub actions. +Releases are normally cut by the `Build and Upload Python Package` GitHub action, +which is triggered manually and takes the new version number as its input. It +bumps the version, formats the repository, creates the tag and release, and +uploads to PyPI and the `cnapy` Anaconda channel. The steps below are the manual +equivalent, for the case where the action cannot be used. ## In any case: -1. Update version number in `conda-recipe/meta.yaml`. Update dependencies/versions and description if necessary -2. Update version number in `setup.py`. Update dependencies/versions and description and `requirements.txt` if necessary. -3. Create a new tag/release on GitHub with matching version number (e.g. `v0.1`) +1. Update the version number in `pyproject.toml`, `conda-recipe/meta.yaml` and + `docs/source/conf.py`. `python .github/update_version.py ` + does this per file and fails if the file declares no version, so a silent + miss is not possible. Update dependencies/versions and the description in + `pyproject.toml` and `requirements.txt` if necessary. +2. Create a new tag/release on GitHub with a matching version number (e.g. `v0.1`) -## Building PyPi package +## Building the PyPI package ### Prerequisites -1. Install newest version of pip (`python -m pip install --upgrade pip` or `conda update pip`) -2. Install twain (`pip install twain` or `conda install twain`) +1. Install the newest version of pip (`python -m pip install --upgrade pip` or `conda update pip`) +2. Install the build frontend and twine (`pip install --upgrade build twine`) ### Build and upload package -1. Navigate to package folder and build package with `python setup.py sdist bdist_wheel` -2. Clean up dist folder (remove old version builds) -3. Upload package source and wheel to PyPi via `twine upload dist/*` (Use PyPi credencials) +1. Clean the dist folder (remove old version builds) +2. Navigate to the package folder and build with `python -m build`, which produces + both the source distribution and the wheel +3. Verify the metadata renders on PyPI with `twine check dist/*` +4. Upload the source distribution and wheel via `twine upload dist/*` (use PyPI credentials) -## Building Conda package +## Building the Conda package ### Prerequisites 1. Create a conda environment to build the package (e.g. `conda create -n straindesign-build`) -2. Activate environment (`conda activate straindesign-build`) -3. Install requirements (`conda install anaconda-client conda-build`) +2. Activate the environment (`conda activate straindesign-build`) +3. Install the requirements (`conda install anaconda-client conda-build`) ### Build and upload package -1. Navigate to package folder and build package with `conda-build conda-recipe/. -c conda-forge --croot conda-bld` -2. Clean up conda-bld folder (remove old version builds) +1. Navigate to the package folder and build with `conda-build conda-recipe/. -c conda-forge --croot conda-bld` +2. Clean up the conda-bld folder (remove old version builds) 3. `anaconda login` 4. `anaconda upload -u cnapy conda-bld/noarch/straindesign*` 5. Clean up with conda diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index a13e4a7..b8245de 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -13,13 +13,18 @@ requirements: build: - python - pip - - setuptools >=64 + - setuptools >=77 - wheel run: - python - scipy - - matplotlib + - matplotlib-base - psutil + - numpy + - pandas + - optlang + - swiglpk + - pyscipopt - conda-forge:cobra build: @@ -33,8 +38,8 @@ test: about: home: https://github.com/klamt-lab/straindesign - license: Apache 2 - license_family: MIT + license: Apache-2.0 + license_family: Apache license_file: LICENSE summary: Computational strain design package for the COBRApy framework. doc_url: https://github.com/klamt-lab/straindesign diff --git a/environment.yml b/environment.yml index 0f70338..4e894c3 100644 --- a/environment.yml +++ b/environment.yml @@ -5,6 +5,11 @@ channels: dependencies: - python - scipy - - matplotlib + - matplotlib-base - psutil + - numpy + - pandas + - optlang + - swiglpk + - pyscipopt - conda-forge::cobra diff --git a/pyproject.toml b/pyproject.toml index d4265de..eaec392 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,12 @@ [build-system] -requires = ["setuptools>=64", "wheel"] +requires = ["setuptools>=77", "wheel"] build-backend = "setuptools.build_meta" [project] name = "straindesign" version = "1.18" description = "Computational strain design package for the COBRApy framework" -readme = {text = "Computational strain design package for the COBRApy framework, offering standard and advanced tools for the analysis and redesign of biological networks", content-type = "text/plain"} +readme = "README.md" license = "Apache-2.0" requires-python = ">=3.10" authors = [ @@ -29,6 +29,20 @@ dependencies = [ "scipy", "matplotlib", "psutil", + # Imported directly by this package, but every one of them is already a hard + # requirement of cobra. They are listed so the imports are declared, and left + # deliberately unpinned: contributing no version constraint leaves cobra the + # sole authority on which versions get resolved. Do not add bounds here -- + # that would start a fight with cobra over versions we have no opinion on. + "numpy", + "pandas", + "optlang", + "swiglpk", + # The only free solver that handles indicator constraints natively. GLPK has + # to translate them into big-M, which is documented to give incorrect results + # (see glpk_interface.py and test_07_compression.py), so without this a + # default install would have no solver able to express the formulation. + "pyscipopt", ] [project.urls] diff --git a/requirements.txt b/requirements.txt index c74b88b..168b0c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,8 @@ cobra scipy matplotlib psutil +numpy +pandas +optlang +swiglpk +pyscipopt diff --git a/straindesign/__init__.py b/straindesign/__init__.py index ac79c2f..60c4f92 100644 --- a/straindesign/__init__.py +++ b/straindesign/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/compression.py b/straindesign/compression.py index 8d450c9..5c08692 100644 --- a/straindesign/compression.py +++ b/straindesign/compression.py @@ -1,3 +1,19 @@ +#!/usr/bin/env python3 +# +# Copyright 2025-2026 Max Planck Institute Magdeburg +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# """ Metabolic network compression using nullspace-based coupling detection. diff --git a/straindesign/compute_strain_designs.py b/straindesign/compute_strain_designs.py index 5cfd258..6db62c6 100644 --- a/straindesign/compute_strain_designs.py +++ b/straindesign/compute_strain_designs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/cplex_interface.py b/straindesign/cplex_interface.py index d652e17..11453d4 100644 --- a/straindesign/cplex_interface.py +++ b/straindesign/cplex_interface.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/glpk_interface.py b/straindesign/glpk_interface.py index 2fd6a12..90ebaab 100644 --- a/straindesign/glpk_interface.py +++ b/straindesign/glpk_interface.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/gurobi_interface.py b/straindesign/gurobi_interface.py index 3262320..a9aa730 100644 --- a/straindesign/gurobi_interface.py +++ b/straindesign/gurobi_interface.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/indicatorConstraints.py b/straindesign/indicatorConstraints.py index cfa2401..13ff132 100644 --- a/straindesign/indicatorConstraints.py +++ b/straindesign/indicatorConstraints.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/lptools.py b/straindesign/lptools.py index 663869f..1ba04fe 100644 --- a/straindesign/lptools.py +++ b/straindesign/lptools.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/names.py b/straindesign/names.py index eabdfdc..64dd5f8 100644 --- a/straindesign/names.py +++ b/straindesign/names.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/networktools.py b/straindesign/networktools.py index f7b0356..060dc88 100644 --- a/straindesign/networktools.py +++ b/straindesign/networktools.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/parse_constr.py b/straindesign/parse_constr.py index cb7b89e..ce628e2 100644 --- a/straindesign/parse_constr.py +++ b/straindesign/parse_constr.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/pool.py b/straindesign/pool.py index 1253512..c909547 100644 --- a/straindesign/pool.py +++ b/straindesign/pool.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/scip_interface.py b/straindesign/scip_interface.py index 4123d88..8798918 100644 --- a/straindesign/scip_interface.py +++ b/straindesign/scip_interface.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/solver_interface.py b/straindesign/solver_interface.py index 4096530..b89a074 100644 --- a/straindesign/solver_interface.py +++ b/straindesign/solver_interface.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/speedy_fva.py b/straindesign/speedy_fva.py index 7c14dfb..50be8f0 100644 --- a/straindesign/speedy_fva.py +++ b/straindesign/speedy_fva.py @@ -1,3 +1,19 @@ +#!/usr/bin/env python3 +# +# Copyright 2026 Max Planck Institute Magdeburg +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# """Accelerated FVA using scan LPs and bound scanning. Standard FVA solves 2*n independent LPs (max and min for each reaction). diff --git a/straindesign/strainDesignMILP.py b/straindesign/strainDesignMILP.py index 60d93d6..325e77e 100644 --- a/straindesign/strainDesignMILP.py +++ b/straindesign/strainDesignMILP.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/strainDesignModule.py b/straindesign/strainDesignModule.py index 478a467..df24d3f 100644 --- a/straindesign/strainDesignModule.py +++ b/straindesign/strainDesignModule.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/strainDesignProblem.py b/straindesign/strainDesignProblem.py index c7031d9..d994c5e 100644 --- a/straindesign/strainDesignProblem.py +++ b/straindesign/strainDesignProblem.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/straindesign/strainDesignSolutions.py b/straindesign/strainDesignSolutions.py index e2b871a..2d86e51 100644 --- a/straindesign/strainDesignSolutions.py +++ b/straindesign/strainDesignSolutions.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 Max Planck Insitute Magdeburg +# Copyright 2022-2026 Max Planck Institute Magdeburg # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.