From c8fc32ae41ccbfd213a7168cddb2e0900c64b2d7 Mon Sep 17 00:00:00 2001 From: "Tristan F.-R." Date: Wed, 29 Apr 2026 05:52:32 +0000 Subject: [PATCH 01/10] chore: update dependencies --- environment.yml | 32 ++++++++++++++++---------------- pyproject.toml | 26 +++++++++++++------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/environment.yml b/environment.yml index 4361834a..9c28badd 100644 --- a/environment.yml +++ b/environment.yml @@ -3,16 +3,16 @@ channels: - conda-forge dependencies: - adjusttext=1.3.0 - - bioconda::snakemake-minimal=9.6.2 + - bioconda::snakemake-minimal=9.19.0 # Conda refers to pypi/docker as docker-py. - docker-py=7.1.0 - - matplotlib=3.10.3 - - networkx=3.5 - - pandas=2.3.0 - - pydantic=2.11.7 - - numpy=2.3.1 - - requests=2.32.4 - - scikit-learn=1.7.0 + - matplotlib=3.10.9 + - networkx=3.6.1 + - pandas=3.0.2 + - pydantic=2.13.3 + - numpy=2.4.4 + - requests=2.33.1 + - scikit-learn=1.8.0 - seaborn=0.13.2 - spython=0.3.14 @@ -24,16 +24,16 @@ dependencies: - tabulate=0.9.0 # toolchain deps - - pip=25.3 + - pip=26.1 # This should be the same as requires-python minus the >=. - - python=3.11 + - python=3.13 # development dependencies - - pre-commit=4.2.0 - - pytest=8.4.1 - - pytest-split=0.10.0 - - sphinx=7.4.7 - - sphinx-rtd-theme=2.0.0 + - pre-commit=4.6.0 + - pytest=9.0.3 + - pytest-split=0.11.0 + - sphinx=9.1.0 + - sphinx-rtd-theme=3.1.0 - pip: - - dsub==0.4.13 + - dsub==0.5.2 diff --git a/pyproject.toml b/pyproject.toml index bfc602c6..46166ca8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,29 +18,29 @@ classifiers = [ ] requires-python = ">=3.11" dependencies = [ - "adjusttext==0.7.3", - "snakemake==9.6.2", + "adjusttext==1.3.0", + "snakemake==9.19.0", "docker==7.1.0", - "matplotlib==3.10.3", - "networkx==3.5", - "pandas==2.3.0", - "pydantic==2.11.7", - "numpy==2.3.1", - "requests==2.32.4", - "scikit-learn==1.7.0", + "matplotlib==3.10.9", + "networkx==3.6.1", + "pandas==3.0.2", + "pydantic==2.13.3", + "numpy==2.4.4", + "requests==2.33.1", + "scikit-learn==1.8.0", "seaborn==0.13.2", "spython==0.3.14", # toolchain deps - "pip==25.3", + "pip==26.1", ] [project.optional-dependencies] dev = [ # Only required for development - "pre-commit==4.2.0", - "pytest==8.4.1", - "pytest-split==0.10.0", + "pre-commit==4.6.0", + "pytest==9.0.3", + "pytest-split==0.11.0", ] [project.urls] From 7b3e383853676c0c7471b8962f34010cb280de9e Mon Sep 17 00:00:00 2001 From: "Tristan F.-R." Date: Thu, 30 Apr 2026 21:24:31 +0000 Subject: [PATCH 02/10] fix: bump python to 3.13 --- .pre-commit-config.yaml | 2 +- .readthedocs.yaml | 2 +- environment.yml | 10 +++++----- pyproject.toml | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe010814..3b0581af 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks default_language_version: # Match this to the version specified in environment.yml - python: python3.11 + python: python3.13 repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 # Use the ref you want to point at diff --git a/.readthedocs.yaml b/.readthedocs.yaml index cd6d3883..612c1d92 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,7 +8,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.13" # Build documentation in the "docs/" directory with Sphinx sphinx: diff --git a/environment.yml b/environment.yml index 9c28badd..197fe214 100644 --- a/environment.yml +++ b/environment.yml @@ -18,13 +18,13 @@ dependencies: # conda-specific for dsub - python-dateutil=2.9.0 - - pytz=2025.2 - - pyyaml=6.0.2 - - tenacity=9.1.2 - - tabulate=0.9.0 + - pytz=2026.1 + - pyyaml=6.0.3 + - tenacity=9.1.4 + - tabulate=0.10.0 # toolchain deps - - pip=26.1 + - pip=26.0.1 # This should be the same as requires-python minus the >=. - python=3.13 diff --git a/pyproject.toml b/pyproject.toml index 46166ca8..45f9b572 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ classifiers = [ "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering :: Bio-Informatics", ] -requires-python = ">=3.11" +requires-python = ">=3.13" dependencies = [ "adjusttext==1.3.0", "snakemake==9.19.0", @@ -32,7 +32,7 @@ dependencies = [ "spython==0.3.14", # toolchain deps - "pip==26.1", + "pip==26.0.1", ] [project.optional-dependencies] @@ -52,7 +52,7 @@ requires = ["setuptools>=64.0"] build-backend = "setuptools.build_meta" [tool.ruff] -target-version = "py311" +target-version = "py313" # Autofix errors when possible fix = true # Select categories or specific rules from https://beta.ruff.rs/docs/rules/ From 641df788aee2d954f631b8e91bc7bc3b05c87c5b Mon Sep 17 00:00:00 2001 From: "Tristan F.-R." Date: Thu, 30 Apr 2026 21:26:35 +0000 Subject: [PATCH 03/10] fix: bump SPRAS image to Python 3.13 --- docker-wrappers/SPRAS/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-wrappers/SPRAS/Dockerfile b/docker-wrappers/SPRAS/Dockerfile index 3d69943c..4bb0d660 100644 --- a/docker-wrappers/SPRAS/Dockerfile +++ b/docker-wrappers/SPRAS/Dockerfile @@ -4,7 +4,7 @@ FROM almalinux:9 RUN dnf update -y && \ dnf install -y epel-release && \ dnf install -y gcc gcc-c++ \ - python3.11 python3.11-pip python3.11-devel \ + python3.13 python3.13-pip python3.13-devel \ docker apptainer && \ dnf clean all @@ -13,4 +13,4 @@ RUN chmod -R 777 /spras WORKDIR /spras # Install spras into the container -RUN pip3.11 install . +RUN pip3.13 install . From d5ea035e7a3fc24de77206888721fc13d8ffa583 Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Mon, 4 May 2026 22:38:18 +0000 Subject: [PATCH 04/10] chore: bump down dsub --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 197fe214..f069b1c8 100644 --- a/environment.yml +++ b/environment.yml @@ -36,4 +36,4 @@ dependencies: - sphinx-rtd-theme=3.1.0 - pip: - - dsub==0.5.2 + - dsub==0.4.13 From 8629e0976ff78208032f6695993c415de70ffac3 Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Mon, 4 May 2026 22:44:19 +0000 Subject: [PATCH 05/10] refactor: move generic parameter validation into its `get_params_generic` getter --- spras/prm.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/spras/prm.py b/spras/prm.py index 18f3c8a9..63516d5f 100644 --- a/spras/prm.py +++ b/spras/prm.py @@ -1,7 +1,8 @@ import os from abc import ABC, abstractmethod from pathlib import Path -from typing import Any, Generic, Mapping, Optional, TypeVar, cast, get_args +from types import get_original_bases +from typing import Any, Generic, Mapping, Optional, TypeVar, cast from pydantic import BaseModel @@ -52,10 +53,19 @@ def get_params_generic(cls) -> type[T]: For example, on `class PathLinker(PRM[PathLinkerParams])`, calling `PathLinker.get_params_generic()` returns `PathLinkerParams`. """ - # TODO: use the type-safe get_original_bases when we bump to >= Python 3.12 - # This is hacky reflection from https://stackoverflow.com/a/71720366/7589775 - # which grabs the class of type T by the definition of `__orig_bases__`. - return get_args(cast(Any, cls).__orig_bases__[0])[0] + original_bases = get_original_bases(cls) + + # Since we just used reflection, we provide a few mountain-dewey error messages here + # to protect against any developer confusion. + assert len(original_bases) == 1, "There were several generics passed into PRM, when precisely one is required." + T_class = original_bases[0] + + if not issubclass(T_class, BaseModel): + raise RuntimeError("The generic passed into PRM is not a pydantic.BaseModel.") + + # We finalize with a cast, since issubclass does an over-eager casting to + # its specified type. + return cast(type[T], T_class) # This is used in `runner.py` to avoid a dependency diamond when trying # to import the actual algorithm schema. @@ -66,11 +76,6 @@ def run_typeless(cls, inputs: dict[str, str | os.PathLike], output_file: str | o """ T_class = cls.get_params_generic() - # Since we just used reflection, we provide a mountain-dewey error message here - # to protect against any developer confusion. - if not issubclass(T_class, BaseModel): - raise RuntimeError("The generic passed into PRM is not a pydantic.BaseModel.") - # Validates our untyped `args` parameter against our parameter class of type T # using BaseModel.model_validate (https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_validate) # (Pydantic already provides nice error messages, so we don't need to worry about catching this.) From cfa90119b3d1a26c5790c36bec598d33d372272e Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Mon, 4 May 2026 22:53:58 +0000 Subject: [PATCH 06/10] fix: drop useless cast i was Very wrong about that --- spras/prm.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spras/prm.py b/spras/prm.py index 63516d5f..a68074d1 100644 --- a/spras/prm.py +++ b/spras/prm.py @@ -60,12 +60,10 @@ def get_params_generic(cls) -> type[T]: assert len(original_bases) == 1, "There were several generics passed into PRM, when precisely one is required." T_class = original_bases[0] - if not issubclass(T_class, BaseModel): + if not issubclass(BaseModel, T_class): raise RuntimeError("The generic passed into PRM is not a pydantic.BaseModel.") - # We finalize with a cast, since issubclass does an over-eager casting to - # its specified type. - return cast(type[T], T_class) + return T_class # This is used in `runner.py` to avoid a dependency diamond when trying # to import the actual algorithm schema. From 1b4b789ec0fe509f1e2dde3ffef8edcdbd36b185 Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Tue, 5 May 2026 01:23:18 +0000 Subject: [PATCH 07/10] fix: actually correct subclass logic get_original_bases does _not_ have the same signature as its internal reflection counterpart! --- spras/dataset.py | 5 ++--- spras/prm.py | 15 ++++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/spras/dataset.py b/spras/dataset.py index ddf74736..3e9975f3 100644 --- a/spras/dataset.py +++ b/spras/dataset.py @@ -1,7 +1,7 @@ import os import pickle as pkl import warnings -from typing import Union +from typing import Self, Union import pandas as pd @@ -60,9 +60,8 @@ def to_file(self, file: LoosePathLike): with open(file, "wb") as f: pkl.dump(self, f) - # NOTE: When we bump to Python 3.13, we can use the reference Dataset instead of the literal "Dataset" for typing. @classmethod - def from_file(cls, file: Union[LoosePathLike, "Dataset"]): + def from_file(cls, file: Union[LoosePathLike, Self]): """ Loads dataset object from a pickle file or another `Dataset` object. Usage: dataset = Dataset.from_file(pickle_file) diff --git a/spras/prm.py b/spras/prm.py index a68074d1..5d595e0c 100644 --- a/spras/prm.py +++ b/spras/prm.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from pathlib import Path from types import get_original_bases -from typing import Any, Generic, Mapping, Optional, TypeVar, cast +from typing import Any, Generic, Mapping, Optional, TypeVar, cast, get_args from pydantic import BaseModel @@ -53,17 +53,22 @@ def get_params_generic(cls) -> type[T]: For example, on `class PathLinker(PRM[PathLinkerParams])`, calling `PathLinker.get_params_generic()` returns `PathLinkerParams`. """ + # This gives us (PRM[PathLinkerParams], ) original_bases = get_original_bases(cls) # Since we just used reflection, we provide a few mountain-dewey error messages here # to protect against any developer confusion. - assert len(original_bases) == 1, "There were several generics passed into PRM, when precisely one is required." - T_class = original_bases[0] + assert len(original_bases) == 1, f"{cls} inherits from several classes, when precisely one is required." + original_bases_args = get_args(original_bases[0]) + assert len(original_bases_args) == 1, "There were several generics passed into PRM, when precisely one is required." + T_class, = original_bases_args - if not issubclass(BaseModel, T_class): + if not issubclass(T_class, BaseModel): raise RuntimeError("The generic passed into PRM is not a pydantic.BaseModel.") - return T_class + # Finally, we cast, since issubclass overeagerly restricts T_class to type[BaseModel] + # instead of type[T] without imposing the restriction that T inherits from BaseModel + return cast(type[T], T_class) # This is used in `runner.py` to avoid a dependency diamond when trying # to import the actual algorithm schema. From 41ecd1c5d80dbf92cd8dd3060b4bc688252bcf43 Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Tue, 5 May 2026 01:30:06 +0000 Subject: [PATCH 08/10] fix(responsenet): drop use of axis on drop pandas requires precisely one of (argument-based list specification) or (axis specification) --- spras/responsenet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spras/responsenet.py b/spras/responsenet.py index f53b5984..f8bc3598 100644 --- a/spras/responsenet.py +++ b/spras/responsenet.py @@ -136,7 +136,7 @@ def parse_output(raw_pathway_file, standardized_pathway_file, params): df = raw_pathway_df(raw_pathway_file, sep='\t', header=0) if not df.empty: df.columns = ['Node1', 'Node2', 'Flow'] - df = df.drop(columns=['Flow'], axis=1) + df = df.drop(columns=['Flow']) df = add_rank_column(df) # ResponseNet's outputs should be treated as undirected outputs. df = reinsert_direction_col_undirected(df) From 5da3967eda45c4bf42958ddbc22565d9eff6e4a8 Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Tue, 5 May 2026 04:29:23 +0000 Subject: [PATCH 09/10] ci: bump miniconda Trying to resolve mac errors --- .github/workflows/release.yml | 2 +- .github/workflows/test-spras.yml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf4ee615..fbb30271 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/test-spras.yml b/.github/workflows/test-spras.yml index db6d1b0f..b5bd1b74 100644 --- a/.github/workflows/test-spras.yml +++ b/.github/workflows/test-spras.yml @@ -12,13 +12,13 @@ jobs: os: [macos-latest, windows-latest] steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install conda environment - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v4 with: activate-environment: spras environment-file: environment.yml - auto-activate-base: false + auto-activate: false miniconda-version: 'latest' - name: Log conda environment # Log conda environment contents @@ -49,11 +49,11 @@ jobs: sudo docker image prune --all --force sudo docker builder prune -a - name: Install conda environment - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v4 with: activate-environment: spras environment-file: environment.yml - auto-activate-base: false + auto-activate: false miniconda-version: 'latest' - name: Install spras in conda env # Install spras in the environment using pip @@ -85,11 +85,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Install conda environment - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v4 with: activate-environment: spras environment-file: environment.yml - auto-activate-base: false + auto-activate: false miniconda-version: 'latest' # Install spras in the environment using pip - name: Install spras in conda env @@ -110,11 +110,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Install conda environment - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v4 with: activate-environment: spras environment-file: environment.yml - auto-activate-base: false + auto-activate: false miniconda-version: 'latest' - name: Run pre-commit shell: bash --login {0} From b9728180761ebe0b3370108fc9841f7091ecb95b Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Sun, 10 May 2026 23:32:24 +0000 Subject: [PATCH 10/10] chore: bump snakemake --- environment.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index f069b1c8..12dfd2fb 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge dependencies: - adjusttext=1.3.0 - - bioconda::snakemake-minimal=9.19.0 + - bioconda::snakemake-minimal=9.20.0 # Conda refers to pypi/docker as docker-py. - docker-py=7.1.0 - matplotlib=3.10.9 diff --git a/pyproject.toml b/pyproject.toml index 45f9b572..d28d4cfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ requires-python = ">=3.13" dependencies = [ "adjusttext==1.3.0", - "snakemake==9.19.0", + "snakemake==9.20.0", "docker==7.1.0", "matplotlib==3.10.9", "networkx==3.6.1",