diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 6ec1cfe8..e708fcb4 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -8,8 +8,8 @@ jobs:
- uses: chartboost/ruff-action@v1
with:
src: './pyenzyme'
- version: 0.15.22 # keep in sync with .pre-commit-config.yaml
+ version: 0.16.10 # keep in sync with .pre-commit-config.yaml
- uses: chartboost/ruff-action@v1
with:
src: './tests'
- version: 0.15.22
+ version: 0.16.10
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6fc4ef38..75d7fdcd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,7 +7,7 @@ repos:
- id: uv-lock
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.15.22
+ rev: v0.16.10
hooks:
# Same lint gate CI runs (lint.yml lints ./pyenzyme and ./tests only).
- id: ruff-check
diff --git a/pyenzyme/__init__.py b/pyenzyme/__init__.py
index 73ba22b5..7b0ba12a 100644
--- a/pyenzyme/__init__.py
+++ b/pyenzyme/__init__.py
@@ -28,23 +28,23 @@
write_enzymeml = EnzymeMLHandler.write_enzymeml
__all__ = [
+ "EnzymeMLHandler",
+ "EnzymeMLSuite",
"UnitDefinition",
"UnitType",
- "EnzymeMLSuite",
- "EnzymeMLHandler",
+ "compose",
"from_csv",
"from_dataframe",
"from_excel",
"from_sbml",
+ "group_measurements",
+ "plot",
+ "plot_interactive",
"read_enzymeml",
+ "summary",
"to_pandas",
"to_sbml",
"write_enzymeml",
- "compose",
- "plot",
- "plot_interactive",
- "summary",
- "group_measurements",
]
__version__ = version("pyenzyme")
diff --git a/pyenzyme/composer.py b/pyenzyme/composer.py
index 21db6789..9f8eedc4 100644
--- a/pyenzyme/composer.py
+++ b/pyenzyme/composer.py
@@ -1,4 +1,5 @@
-from typing import List, Optional, Tuple, Callable, Any
+from collections.abc import Callable
+from typing import Any
from rich.console import Console
@@ -28,11 +29,11 @@
def compose(
name: str,
- proteins: Optional[list[str]] = None,
- small_molecules: Optional[list[str]] = None,
- reactions: Optional[list[str]] = None,
- vessel: Optional[v2.Vessel] = None,
- id_mapping: Optional[dict[str, str]] = None,
+ proteins: list[str] | None = None,
+ small_molecules: list[str] | None = None,
+ reactions: list[str] | None = None,
+ vessel: v2.Vessel | None = None,
+ id_mapping: dict[str, str] | None = None,
) -> v2.EnzymeMLDocument:
"""
Compose an EnzymeML document from proteins, small molecules, and reactions.
@@ -162,7 +163,7 @@ def _fetch_small_molecule(small_molecule_id: str) -> v2.SmallMolecule:
)
-def _fetch_reaction(reaction_id: str) -> Tuple[v2.Reaction, List[v2.SmallMolecule]]:
+def _fetch_reaction(reaction_id: str) -> tuple[v2.Reaction, list[v2.SmallMolecule]]:
"""
Fetch reaction information using available fetchers.
@@ -179,7 +180,7 @@ def _fetch_reaction(reaction_id: str) -> Tuple[v2.Reaction, List[v2.SmallMolecul
def _fetch_with_fetchers(
- entity_id: str, fetchers: List[Callable], entity_type: str
+ entity_id: str, fetchers: list[Callable], entity_type: str
) -> Any:
"""
Generic function to attempt fetching with multiple fetchers.
@@ -210,7 +211,7 @@ def _fetch_with_fetchers(
) from last_error
-def _remove_duplicates(objects: List[Any]) -> List[Any]:
+def _remove_duplicates(objects: list[Any]) -> list[Any]:
"""
Remove duplicate objects based on their ID attribute.
@@ -235,7 +236,7 @@ def _remove_duplicates(objects: List[Any]) -> List[Any]:
def _apply_id_mapping(
- objects: List[Any],
+ objects: list[Any],
id_mapping: dict[str, str],
) -> dict[str, str]:
"""
diff --git a/pyenzyme/equations/__init__.py b/pyenzyme/equations/__init__.py
index 0965ff95..c1c420fc 100644
--- a/pyenzyme/equations/__init__.py
+++ b/pyenzyme/equations/__init__.py
@@ -1,4 +1,4 @@
-from .math import build_equation, build_equations # noqa: F401
-from .chem import build_reaction, build_reactions # noqa: F401
+from .chem import build_reaction, build_reactions
+from .math import build_equation, build_equations
__all__ = ["build_equation", "build_equations", "build_reaction", "build_reactions"]
diff --git a/pyenzyme/equations/math.py b/pyenzyme/equations/math.py
index f5eea3be..ad694242 100644
--- a/pyenzyme/equations/math.py
+++ b/pyenzyme/equations/math.py
@@ -2,10 +2,10 @@
import rich
from loguru import logger
+from mdmodels.units.unit_definition import UnitDefinition
from sympy import sympify
from pyenzyme.logging import add_logger
-from mdmodels.units.unit_definition import UnitDefinition
from pyenzyme.versions.v2 import (
EnzymeMLDocument,
Equation,
diff --git a/pyenzyme/fetcher/__init__.py b/pyenzyme/fetcher/__init__.py
index ed1ec769..2c82fe75 100644
--- a/pyenzyme/fetcher/__init__.py
+++ b/pyenzyme/fetcher/__init__.py
@@ -1,13 +1,13 @@
from .chebi import fetch_chebi
from .pdb import fetch_pdb
from .pubchem import fetch_pubchem
-from .uniprot import fetch_uniprot
from .rhea import fetch_rhea
+from .uniprot import fetch_uniprot
__all__ = [
"fetch_chebi",
"fetch_pdb",
"fetch_pubchem",
- "fetch_uniprot",
"fetch_rhea",
+ "fetch_uniprot",
]
diff --git a/pyenzyme/fetcher/chebi.py b/pyenzyme/fetcher/chebi.py
index 6e622965..60f312de 100644
--- a/pyenzyme/fetcher/chebi.py
+++ b/pyenzyme/fetcher/chebi.py
@@ -6,7 +6,6 @@
"""
import re
-from typing import List, Optional
import httpx
from pydantic import BaseModel, ConfigDict, Field
@@ -19,7 +18,7 @@
class ChEBIError(Exception):
"""Error class for ChEBI-specific errors."""
- def __init__(self, message: str, cause: Optional[Exception] = None):
+ def __init__(self, message: str, cause: Exception | None = None):
super().__init__(message)
self.cause = cause
@@ -30,19 +29,19 @@ class ChebiSearchSource(BaseModel):
model_config = ConfigDict(extra="ignore")
chebi_accession: str
- name: Optional[str] = None
+ name: str | None = None
ascii_name: str
- smiles: Optional[str] = None
- inchi: Optional[str] = None
- inchikey: Optional[str] = None
- definition: Optional[str] = None
- formula: Optional[str] = None
- charge: Optional[int] = None
- mass: Optional[float] = None
- monoisotopicmass: Optional[float] = None
- stars: Optional[int] = None
- default_structure: Optional[int] = None
- structures: Optional[List[int]] = None
+ smiles: str | None = None
+ inchi: str | None = None
+ inchikey: str | None = None
+ definition: str | None = None
+ formula: str | None = None
+ charge: int | None = None
+ mass: float | None = None
+ monoisotopicmass: float | None = None
+ stars: int | None = None
+ default_structure: int | None = None
+ structures: list[int] | None = None
class ChebiSearchResult(BaseModel):
@@ -56,7 +55,7 @@ class ChebiSearchResult(BaseModel):
class ChebiSearchResponse(BaseModel):
"""Search response structure from ChEBI search API."""
- results: List[ChebiSearchResult]
+ results: list[ChebiSearchResult]
total: int
number_pages: int
@@ -105,14 +104,14 @@ def get_entry_by_id(self, chebi_id: str) -> ChebiSearchSource:
except Exception as e:
if isinstance(e, ChEBIError):
raise e
- raise ChEBIError(f"Failed to parse ChEBI response: {str(e)}", e)
+ raise ChEBIError(f"Failed to parse ChEBI response: {e!s}", e)
else:
raise ChEBIError(f"HTTP {response.status_code}: {response.text}")
except httpx.HTTPStatusError as e:
- raise ChEBIError(f"Failed to fetch ChEBI ID {chebi_id}: {str(e)}", e)
+ raise ChEBIError(f"Failed to fetch ChEBI ID {chebi_id}: {e!s}", e)
- def get_entries_batch(self, chebi_ids: List[str]) -> List[ChebiSearchSource]:
+ def get_entries_batch(self, chebi_ids: list[str]) -> list[ChebiSearchSource]:
"""
Fetch multiple ChEBI entries by their IDs using the search API.
@@ -136,13 +135,13 @@ def get_entries_batch(self, chebi_ids: List[str]) -> List[ChebiSearchSource]:
results.append(entry)
except ChEBIError as e:
# Continue with other IDs even if one fails
- raise ChEBIError(f"Failed to fetch ChEBI ID {chebi_id}: {str(e)}", e)
+ raise ChEBIError(f"Failed to fetch ChEBI ID {chebi_id}: {e!s}", e)
return results
def search_entries(
- self, query: str, size: Optional[int] = None, page: int = 1
- ) -> List[ChebiSearchSource]:
+ self, query: str, size: int | None = None, page: int = 1
+ ) -> list[ChebiSearchSource]:
"""
Search for ChEBI entries by query string.
@@ -178,14 +177,14 @@ def search_entries(
except Exception as e:
if isinstance(e, ChEBIError):
raise e
- raise ChEBIError(f"Invalid search response format: {str(e)}", e)
+ raise ChEBIError(f"Invalid search response format: {e!s}", e)
else:
raise ChEBIError(
f"Search failed: HTTP {response.status_code}: {response.text}"
)
except httpx.HTTPStatusError as e:
- raise ChEBIError(f"Failed to search ChEBI: {str(e)}", e)
+ raise ChEBIError(f"Failed to search ChEBI: {e!s}", e)
def process_search_result(source: ChebiSearchSource) -> v2.SmallMolecule:
@@ -227,8 +226,8 @@ def process_search_result(source: ChebiSearchSource) -> v2.SmallMolecule:
def fetch_chebi(
chebi_id: str,
- smallmol_id: Optional[str] = None,
- vessel_id: Optional[str] = None,
+ smallmol_id: str | None = None,
+ vessel_id: str | None = None,
) -> v2.SmallMolecule:
"""
Fetch a ChEBI entry by ID and convert it to a SmallMolecule object.
@@ -264,7 +263,7 @@ def fetch_chebi(
raise ValueError(str(e)) from e
-def fetch_chebi_batch(chebi_ids: List[str]) -> List[v2.SmallMolecule]:
+def fetch_chebi_batch(chebi_ids: list[str]) -> list[v2.SmallMolecule]:
"""
Fetch multiple ChEBI entries by their IDs and convert them to SmallMolecule objects.
@@ -287,7 +286,7 @@ def fetch_chebi_batch(chebi_ids: List[str]) -> List[v2.SmallMolecule]:
return [process_search_result(source) for source in chebi_sources]
-def search_chebi(query: str, size: Optional[int] = None) -> List[v2.SmallMolecule]:
+def search_chebi(query: str, size: int | None = None) -> list[v2.SmallMolecule]:
"""
Search for ChEBI entries by query string.
diff --git a/pyenzyme/fetcher/pdb.py b/pyenzyme/fetcher/pdb.py
index 79146942..1075ec89 100644
--- a/pyenzyme/fetcher/pdb.py
+++ b/pyenzyme/fetcher/pdb.py
@@ -5,7 +5,7 @@
Protein Data Bank by ID and map it to the PyEnzyme data model (v2).
"""
-from typing import Any, Dict, List, Optional
+from typing import Any
import httpx
from pydantic import BaseModel, Field
@@ -19,42 +19,42 @@
class Citation(BaseModel):
"""Model for PDB citation data"""
- title: Optional[str] = None
- authors: Optional[List[str]] = None
- journal_name: Optional[str] = Field(default=None, alias="journal_abbrev")
- year: Optional[int] = None
- doi: Optional[str] = None
- pubmed_id: Optional[int] = Field(default=None, alias="pdbx_database_id_PubMed")
+ title: str | None = None
+ authors: list[str] | None = None
+ journal_name: str | None = Field(default=None, alias="journal_abbrev")
+ year: int | None = None
+ doi: str | None = None
+ pubmed_id: int | None = Field(default=None, alias="pdbx_database_id_PubMed")
class StructInfo(BaseModel):
"""Model for PDB structure information"""
- title: Optional[str] = None
- experimental_method: Optional[str] = None
- resolution: Optional[float] = None
+ title: str | None = None
+ experimental_method: str | None = None
+ resolution: float | None = None
class EntityInfo(BaseModel):
"""Model for PDB entity information"""
- description: Optional[str] = None
- polymer_type: Optional[str] = None
- ec_number: Optional[str] = None
- sequence: Optional[str] = None
- organism_scientific_name: Optional[str] = None
- organism_taxid: Optional[int] = None
+ description: str | None = None
+ polymer_type: str | None = None
+ ec_number: str | None = None
+ sequence: str | None = None
+ organism_scientific_name: str | None = None
+ organism_taxid: int | None = None
class PDBResponse(BaseModel):
"""Model for PDB API response"""
pdb_id: str
- citation: List[Citation] = []
- struct: Optional[StructInfo] = None
- polymer_entities: Optional[Dict[str, EntityInfo]] = None
- rcsb_primary_citation: Optional[Dict[str, Any]] = None
- rcsb_entry_info: Optional[Dict[str, Any]] = None
+ citation: list[Citation] = []
+ struct: StructInfo | None = None
+ polymer_entities: dict[str, EntityInfo] | None = None
+ rcsb_primary_citation: dict[str, Any] | None = None
+ rcsb_entry_info: dict[str, Any] | None = None
class PDBClient:
@@ -62,7 +62,7 @@ class PDBClient:
BASE_URL = "https://data.rcsb.org/rest/v1/core"
- def get_entry_by_id(self, pdb_id: str) -> Optional[PDBResponse]:
+ def get_entry_by_id(self, pdb_id: str) -> PDBResponse | None:
"""
Fetch a PDB entry by its ID.
@@ -139,9 +139,9 @@ def get_entry_by_id(self, pdb_id: str) -> Optional[PDBResponse]:
)
except ValueError as e:
- raise ValueError(f"Failed to retrieve PDB entry: {str(e)}")
+ raise ValueError(f"Failed to retrieve PDB entry: {e!s}")
except ConnectionError as e:
- raise ValueError(f"Connection to PDB server failed: {str(e)}")
+ raise ValueError(f"Connection to PDB server failed: {e!s}")
def _fetch_json(self, url: str) -> dict:
"""
@@ -170,16 +170,16 @@ def _fetch_json(self, url: str) -> dict:
)
except httpx.HTTPStatusError as e:
- raise ConnectionError(f"Connection failed: {str(e)}")
+ raise ConnectionError(f"Connection failed: {e!s}")
except ValueError as e:
- raise ValueError(f"Failed to parse response: {str(e)}")
+ raise ValueError(f"Failed to parse response: {e!s}")
def fetch_pdb(
pdb_id: str,
- protein_id: Optional[str] = None,
+ protein_id: str | None = None,
entity_id: str = "1",
- vessel_id: Optional[str] = None,
+ vessel_id: str | None = None,
) -> v2.Protein:
"""
Fetch a PDB entry by ID and convert it to a Protein object.
diff --git a/pyenzyme/fetcher/pubchem.py b/pyenzyme/fetcher/pubchem.py
index d8be93e1..b4f409a3 100644
--- a/pyenzyme/fetcher/pubchem.py
+++ b/pyenzyme/fetcher/pubchem.py
@@ -1,4 +1,4 @@
-from typing import ClassVar, Optional
+from typing import ClassVar
import httpx
from pydantic import BaseModel, Field, field_validator
@@ -15,7 +15,7 @@ class PCUrn(BaseModel):
"""
label: str
- name: Optional[str] = Field(default=None)
+ name: str | None = Field(default=None)
class PCProp(BaseModel):
@@ -104,7 +104,7 @@ def from_cid(cid: int) -> PubChemQuery:
def extract_value(
query: PCCompound,
label: str,
- name: Optional[str] = None,
+ name: str | None = None,
) -> float | int | str | None:
"""
Extracts a specific property value from a PubChem compound.
@@ -149,8 +149,8 @@ def extract_by_preference(
def fetch_pubchem(
cid: str,
- smallmol_id: Optional[str] = None,
- vessel_id: Optional[str] = None,
+ smallmol_id: str | None = None,
+ vessel_id: str | None = None,
) -> v2.SmallMolecule:
"""
Fetches a compound from PubChem by CID and converts it to a SmallMolecule object.
diff --git a/pyenzyme/fetcher/rhea.py b/pyenzyme/fetcher/rhea.py
index a205bf70..6ee225cf 100644
--- a/pyenzyme/fetcher/rhea.py
+++ b/pyenzyme/fetcher/rhea.py
@@ -7,7 +7,7 @@
import re
from io import StringIO
-from typing import ClassVar, List, Optional, Tuple
+from typing import ClassVar
import httpx
import pandas as pd
@@ -46,7 +46,7 @@ class RheaQuery(BaseModel):
"""
count: int
- results: List[RheaResult]
+ results: list[RheaResult]
class RheaClient(BaseModel):
@@ -65,7 +65,7 @@ class RheaClient(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
json_content: RheaResult
- chebi_ids: List[str]
+ chebi_ids: list[str]
BASE_URL: ClassVar[str] = (
"https://www.rhea-db.org/rhea/?query=RHEA:{0}&columns=rhea-id,equation,chebi-id&format={1}&limit=10"
@@ -147,8 +147,8 @@ def fetch_json(query: str) -> RheaQuery:
def fetch_rhea(
rhea_id: str,
- vessel_id: Optional[str] = None,
-) -> Tuple[v2.Reaction, List[v2.SmallMolecule]]:
+ vessel_id: str | None = None,
+) -> tuple[v2.Reaction, list[v2.SmallMolecule]]:
"""
Fetch a Rhea entry by ID and convert it to a Reaction object.
@@ -227,7 +227,7 @@ def fetch_rhea(
return reaction, small_molecules
-def _split_chemical_equation_side(equation_side: str) -> List[str]:
+def _split_chemical_equation_side(equation_side: str) -> list[str]:
"""
Split a chemical equation side on '+' while respecting parentheses.
diff --git a/pyenzyme/fetcher/uniprot.py b/pyenzyme/fetcher/uniprot.py
index fe76b7fb..aee6a83a 100644
--- a/pyenzyme/fetcher/uniprot.py
+++ b/pyenzyme/fetcher/uniprot.py
@@ -5,9 +5,10 @@
UniProt database by ID and map it to the PyEnzyme data model (v2).
"""
+
import requests
-from typing import List, Optional, Union
from pydantic import BaseModel, Field, ValidationError
+
from pyenzyme.fetcher.chebi import process_id
from pyenzyme.versions import v2
@@ -28,21 +29,21 @@ class RecommendedName(BaseModel):
"""Model for a recommended or submitted protein name in UniProt API"""
full_name: ProteinName = Field(alias="fullName")
- ec_numbers: Optional[List[ECNumber]] = Field(default=None, alias="ecNumbers")
+ ec_numbers: list[ECNumber] | None = Field(default=None, alias="ecNumbers")
class ProteinDescription(BaseModel):
"""Model for protein description in UniProt API"""
- recommended_name: Optional[RecommendedName] = Field(
+ recommended_name: RecommendedName | None = Field(
default=None, alias="recommendedName"
)
- submission_names: List[RecommendedName] = Field(
+ submission_names: list[RecommendedName] = Field(
default_factory=list, alias="submissionNames"
)
@property
- def name(self) -> Optional[RecommendedName]:
+ def name(self) -> RecommendedName | None:
"""Recommended name, or the first submission name for unreviewed (TrEMBL) entries."""
if self.recommended_name:
return self.recommended_name
@@ -52,8 +53,8 @@ def name(self) -> Optional[RecommendedName]:
class Organism(BaseModel):
"""Model for organism in UniProt API"""
- scientific_name: Optional[str] = Field(default=None, alias="scientificName")
- taxon_id: Optional[int] = Field(default=None, alias="taxonId")
+ scientific_name: str | None = Field(default=None, alias="scientificName")
+ taxon_id: int | None = Field(default=None, alias="taxonId")
class Sequence(BaseModel):
@@ -61,7 +62,7 @@ class Sequence(BaseModel):
value: str
length: int
- mol_weight: Optional[int] = Field(default=None, alias="molWeight")
+ mol_weight: int | None = Field(default=None, alias="molWeight")
class UniProtEntry(BaseModel):
@@ -71,10 +72,10 @@ class UniProtEntry(BaseModel):
id: str = Field(alias="uniProtkbId")
protein_description: ProteinDescription = Field(alias="proteinDescription")
- organism: Optional[Organism] = None
- sequence: Optional[Sequence] = None
+ organism: Organism | None = None
+ sequence: Sequence | None = None
accession: str = Field(alias="primaryAccession")
- annotation_score: Optional[float] = Field(default=None, alias="annotationScore")
+ annotation_score: float | None = Field(default=None, alias="annotationScore")
class UniProtClient:
@@ -86,7 +87,7 @@ def __init__(self):
"""Initialize the UniProt client."""
pass
- def get_entry_by_id(self, uniprot_id: str) -> Union[UniProtEntry, None]:
+ def get_entry_by_id(self, uniprot_id: str) -> UniProtEntry | None:
"""
Fetch a UniProt entry by its ID.
@@ -135,8 +136,8 @@ def get_entry_by_id(self, uniprot_id: str) -> Union[UniProtEntry, None]:
def fetch_uniprot(
uniprot_id: str,
- protein_id: Optional[str] = None,
- vessel_id: Optional[str] = None,
+ protein_id: str | None = None,
+ vessel_id: str | None = None,
) -> v2.Protein:
"""
Fetch a UniProt entry by ID and convert it to a Protein object.
diff --git a/pyenzyme/logging.py b/pyenzyme/logging.py
index 781ef209..5ee333e1 100644
--- a/pyenzyme/logging.py
+++ b/pyenzyme/logging.py
@@ -1,4 +1,5 @@
import sys
+
from loguru import logger
@@ -31,11 +32,11 @@ def add_logger(
"""
format = (
- " %s\t{module: >10}\t{level}: {message}" % name
+ f" {name}\t{{module: >10}}\t{{level}}: {{message}}"
)
if isinstance(channel, str):
- format = "{time:YYYY-MM-DD HH:mm:ss} %s {level}: {message}" % name
+ format = f"{{time:YYYY-MM-DD HH:mm:ss}} {name} {{level}}: {{message}}"
logger.remove()
logger.add(
diff --git a/pyenzyme/petab/baserow.py b/pyenzyme/petab/baserow.py
index cc982db3..2b6108cb 100644
--- a/pyenzyme/petab/baserow.py
+++ b/pyenzyme/petab/baserow.py
@@ -1,4 +1,5 @@
from typing import Any
+
from pydantic import BaseModel, ConfigDict
diff --git a/pyenzyme/petab/conditions.py b/pyenzyme/petab/conditions.py
index 0953c9b8..be77b1d2 100644
--- a/pyenzyme/petab/conditions.py
+++ b/pyenzyme/petab/conditions.py
@@ -1,7 +1,9 @@
-from typing import Dict, Any
+from typing import Any
+
from pydantic import Field
from pyenzyme.versions import v2
+
from .baserow import BaseRow
@@ -22,7 +24,7 @@ class ConditionRow(BaseRow):
condition_name: str = Field(alias="conditionName")
species: dict[str, float] = Field(default_factory=dict)
- def to_row(self) -> Dict[str, Any]:
+ def to_row(self) -> dict[str, Any]:
"""
Converts the condition to a dictionary suitable for a PEtab conditions table row.
diff --git a/pyenzyme/petab/io.py b/pyenzyme/petab/io.py
index 8fea2dd7..2614d468 100644
--- a/pyenzyme/petab/io.py
+++ b/pyenzyme/petab/io.py
@@ -1,5 +1,4 @@
from pathlib import Path
-from typing import Union
import pandas as pd
import yaml
@@ -21,7 +20,7 @@
SBML_FILENAME = "model.xml"
-def to_petab(doc: v2.EnzymeMLDocument, path: Union[Path, str]) -> PEtab:
+def to_petab(doc: v2.EnzymeMLDocument, path: Path | str) -> PEtab:
"""
Convert an EnzymeML document to a PEtab parameter estimation problem.
diff --git a/pyenzyme/petab/measurements.py b/pyenzyme/petab/measurements.py
index 586f46d6..ddc4a46a 100644
--- a/pyenzyme/petab/measurements.py
+++ b/pyenzyme/petab/measurements.py
@@ -1,7 +1,8 @@
-from typing import List, Union
+
from pydantic import Field
from pyenzyme.versions import v2
+
from .baserow import BaseRow
@@ -46,11 +47,11 @@ class MeasurementRow(BaseRow):
condition_id: str = Field(alias="simulationConditionId")
measurement: float = Field(alias="measurement")
time: float = Field(alias="time")
- observable_parameters: Union[str, float, None] = Field(
+ observable_parameters: str | float | None = Field(
default=None,
alias="observableParameters",
)
- noise_parameters: Union[str, float, None] = Field(
+ noise_parameters: str | float | None = Field(
default=None,
alias="noiseParameters",
)
@@ -60,7 +61,7 @@ class MeasurementRow(BaseRow):
@classmethod
def from_measurements(
cls, measurements: list[v2.Measurement]
- ) -> List["MeasurementRow"]:
+ ) -> list["MeasurementRow"]:
"""
Convert a list of EnzymeML Measurement objects to a list of PEtab MeasurementRow objects.
"""
@@ -71,7 +72,7 @@ def from_measurements(
]
@classmethod
- def from_measurement(cls, measurement: v2.Measurement) -> List["MeasurementRow"]:
+ def from_measurement(cls, measurement: v2.Measurement) -> list["MeasurementRow"]:
"""
Convert an EnzymeML Measurement object to a list of PEtab MeasurementRow objects.
diff --git a/pyenzyme/petab/observables.py b/pyenzyme/petab/observables.py
index 00a5afc1..c1e284fa 100644
--- a/pyenzyme/petab/observables.py
+++ b/pyenzyme/petab/observables.py
@@ -1,8 +1,9 @@
from enum import Enum
-from typing import List, Optional
+
from pydantic import ConfigDict, Field
from pyenzyme.versions import v2
+
from .baserow import BaseRow
@@ -21,24 +22,24 @@ class ObservableRow(BaseRow):
model_config = ConfigDict(use_enum_values=True)
observable_id: str = Field(alias="observableId")
- observable_name: Optional[str] = Field(alias="observableName", default=None)
+ observable_name: str | None = Field(alias="observableName", default=None)
observable_formula: str = Field(alias="observableFormula")
- observable_noise: Optional[float] = Field(alias="observableNoise", default=0.0)
+ observable_noise: float | None = Field(alias="observableNoise", default=0.0)
observable_transformation: Transformation = Field(
alias="observableTransformation",
default=Transformation.LIN,
)
- noise_formula: Optional[str] = Field(
+ noise_formula: str | None = Field(
default=None,
alias="noiseFormula",
)
- noise_distribution: Optional[NoiseDistribution] = Field(
+ noise_distribution: NoiseDistribution | None = Field(
default=None,
alias="noiseDistribution",
)
@classmethod
- def from_enzymeml(cls, enzmldoc: v2.EnzymeMLDocument) -> List["ObservableRow"]:
+ def from_enzymeml(cls, enzmldoc: v2.EnzymeMLDocument) -> list["ObservableRow"]:
"""Extract observable rows from an EnzymeML document.
Collects all species with data across measurements and creates observable rows.
diff --git a/pyenzyme/petab/parameters.py b/pyenzyme/petab/parameters.py
index 108ca9d4..181b6612 100644
--- a/pyenzyme/petab/parameters.py
+++ b/pyenzyme/petab/parameters.py
@@ -1,8 +1,9 @@
from enum import Enum
-from typing import List, Optional
+
from pydantic import ConfigDict, Field, field_serializer, field_validator
from pyenzyme.versions import v2
+
from .baserow import BaseRow
@@ -47,20 +48,20 @@ class ParameterRow(BaseRow):
model_config = ConfigDict(use_enum_values=True)
parameter_id: str = Field(alias="parameterId")
- parameter_name: Optional[str] = Field(alias="parameterName", default=None)
+ parameter_name: str | None = Field(alias="parameterName", default=None)
parameter_scale: ParameterScale = Field(
default=ParameterScale.LIN,
alias="parameterScale",
)
lower_bound: float = Field(alias="lowerBound")
upper_bound: float = Field(alias="upperBound")
- nominal_value: Optional[float] = Field(alias="nominalValue", default=None)
+ nominal_value: float | None = Field(alias="nominalValue", default=None)
estimate: bool = Field(alias="estimate", default=True)
initialization_prior_type: PriorType = Field(
default=PriorType.PARAMETER_SCALE_UNIFORM,
alias="initializationPriorType",
)
- initialization_prior_parameters: List[float] = Field(
+ initialization_prior_parameters: list[float] = Field(
default_factory=list,
alias="initializationPriorParameters",
)
@@ -68,7 +69,7 @@ class ParameterRow(BaseRow):
default=PriorType.PARAMETER_SCALE_UNIFORM,
alias="objectivePriorType",
)
- objective_prior_parameters: List[float] = Field(
+ objective_prior_parameters: list[float] = Field(
default_factory=list,
alias="objectivePriorParameters",
)
@@ -77,7 +78,7 @@ class ParameterRow(BaseRow):
"objective_prior_parameters",
"initialization_prior_parameters",
)
- def serialize_prior_parameters(self, v: List[float]) -> str:
+ def serialize_prior_parameters(self, v: list[float]) -> str:
"""
Serializes a list of prior parameters into a semicolon-separated string.
@@ -95,7 +96,7 @@ def serialize_prior_parameters(self, v: List[float]) -> str:
mode="before",
)
@classmethod
- def validate_prior_parameters(cls, v: str) -> List[float]:
+ def validate_prior_parameters(cls, v: str) -> list[float]:
"""
Validates and converts a string of prior parameters into a list of floats.
@@ -108,7 +109,7 @@ def validate_prior_parameters(cls, v: str) -> List[float]:
return [float(x) for x in v.split(";")]
@classmethod
- def from_parameters(cls, parameters: List[v2.Parameter]) -> List["ParameterRow"]:
+ def from_parameters(cls, parameters: list[v2.Parameter]) -> list["ParameterRow"]:
"""
Creates a list of ParameterRow objects from a list of PyEnzyme Parameter objects.
"""
diff --git a/pyenzyme/petab/petab.py b/pyenzyme/petab/petab.py
index b0b36a49..b33a7d8b 100644
--- a/pyenzyme/petab/petab.py
+++ b/pyenzyme/petab/petab.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from pathlib import Path
-from typing import List, Union, Optional
+
from pydantic import BaseModel, ConfigDict, Field
@@ -19,12 +19,12 @@ class PEtab(BaseModel):
description="Version of the PEtab format. Only value 1 is supported.",
)
- parameter_file: Union[Path, List[Path]] = Field(
+ parameter_file: Path | list[Path] = Field(
description="File name (absolute or relative) or URL to PEtab parameter table "
"containing parameters of all models listed in `problems`. A single "
"table may be split into multiple files and described as an array here."
)
- problems: List[Problem] = Field(
+ problems: list[Problem] = Field(
default_factory=list,
description="One or multiple PEtab problems (sets of model, condition, observable "
"and measurement files). If different model and data files are "
@@ -35,11 +35,11 @@ class PEtab(BaseModel):
def add_problem(
self,
- sbml_files: List[Path],
- measurement_files: List[Path],
- condition_files: List[Path],
- observable_files: List[Path],
- visualization_files: Optional[List[Path]] = None,
+ sbml_files: list[Path],
+ measurement_files: list[Path],
+ condition_files: list[Path],
+ observable_files: list[Path],
+ visualization_files: list[Path] | None = None,
) -> None:
"""
Add a problem to the PEtab object.
@@ -60,13 +60,13 @@ class Problem(BaseModel):
A set of PEtab model, condition, observable and measurement files and optional visualization files.
"""
- sbml_files: List[Path] = Field(description="List of PEtab SBML files.")
- measurement_files: List[Path] = Field(
+ sbml_files: list[Path] = Field(description="List of PEtab SBML files.")
+ measurement_files: list[Path] = Field(
description="List of PEtab measurement files."
)
- condition_files: List[Path] = Field(description="List of PEtab condition files.")
- observable_files: List[Path] = Field(description="List of PEtab observable files.")
- visualization_files: Optional[List[Path]] = Field(
+ condition_files: list[Path] = Field(description="List of PEtab condition files.")
+ observable_files: list[Path] = Field(description="List of PEtab observable files.")
+ visualization_files: list[Path] | None = Field(
default=None,
description="List of PEtab visualization files.",
)
diff --git a/pyenzyme/plotting/__init__.py b/pyenzyme/plotting/__init__.py
index 1d413c78..58896edc 100644
--- a/pyenzyme/plotting/__init__.py
+++ b/pyenzyme/plotting/__init__.py
@@ -1,4 +1,4 @@
-from .static import plot
from .interactive import plot_interactive
+from .static import plot
__all__ = ["plot", "plot_interactive"]
diff --git a/pyenzyme/plotting/interactive.py b/pyenzyme/plotting/interactive.py
index b5a36dc0..b6a55810 100644
--- a/pyenzyme/plotting/interactive.py
+++ b/pyenzyme/plotting/interactive.py
@@ -1,10 +1,10 @@
-from typing import List, Optional, Tuple, Dict
-from bokeh.models import TabPanel, Tabs, HoverTool, ColumnDataSource
-from bokeh.plotting import figure, show as show_bokeh
+import rich
from bokeh.io import output_file, output_notebook
+from bokeh.models import ColumnDataSource, HoverTool, TabPanel, Tabs
from bokeh.palettes import Category10, Category20
-import rich
+from bokeh.plotting import figure
+from bokeh.plotting import show as show_bokeh
from pyenzyme.thinlayers.base import BaseThinLayer, SimResult, Time
from pyenzyme.versions import v2
@@ -16,9 +16,9 @@
def plot_interactive(
enzmldoc: v2.EnzymeMLDocument,
- measurement_ids: Optional[list[str]] = None,
- thinlayer: Optional[BaseThinLayer] = None,
- out: Optional[str] = None,
+ measurement_ids: list[str] | None = None,
+ thinlayer: BaseThinLayer | None = None,
+ out: str | None = None,
width: int = DEFAULT_WIDTH,
height: int = DEFAULT_HEIGHT,
show: bool = True,
@@ -157,10 +157,10 @@ def _is_notebook() -> bool:
def _create_measurement_tab(
enzmldoc: v2.EnzymeMLDocument,
measurement: v2.Measurement,
- thinlayer: Optional[BaseThinLayer],
+ thinlayer: BaseThinLayer | None,
width: int,
height: int,
- color_map: Dict[str, str],
+ color_map: dict[str, str],
**kwargs,
) -> TabPanel:
"""
@@ -244,8 +244,8 @@ def _plot_species_bokeh(
p: figure,
species: v2.MeasurementData,
pred: SimResult,
- time: List[float],
- color_map: Dict[str, str],
+ time: list[float],
+ color_map: dict[str, str],
**kwargs,
) -> None:
"""
@@ -346,7 +346,7 @@ def _get_fit(
enzmldoc: v2.EnzymeMLDocument,
measurement: v2.Measurement,
thinlayer: BaseThinLayer,
-) -> Tuple[SimResult, Time]:
+) -> tuple[SimResult, Time]:
"""
Get model predictions for a measurement using a thinlayer.
@@ -391,7 +391,7 @@ def _get_fit(
)
-def _collect_dataytypes(measurement_data: List[v2.MeasurementData]) -> List[str]:
+def _collect_dataytypes(measurement_data: list[v2.MeasurementData]) -> list[str]:
"""
Extract and format unique measurement data types.
diff --git a/pyenzyme/plotting/static.py b/pyenzyme/plotting/static.py
index c192b661..68b3b151 100644
--- a/pyenzyme/plotting/static.py
+++ b/pyenzyme/plotting/static.py
@@ -1,10 +1,9 @@
import math
-from typing import List, Optional, Tuple, Union
import numpy as np
from matplotlib import pyplot as plt
-from matplotlib.figure import Figure
from matplotlib.axes import Axes
+from matplotlib.figure import Figure
from pyenzyme.thinlayers.base import BaseThinLayer, SimResult, Time
from pyenzyme.versions import v2
@@ -18,15 +17,15 @@ def plot(
enzmldoc: v2.EnzymeMLDocument,
columns: int = 2,
show: bool = False,
- measurement_ids: Optional[list[str]] = None,
+ measurement_ids: list[str] | None = None,
marker_size: int = 6,
marker_style: str = "o",
- thinlayer: Optional[BaseThinLayer] = None,
- out: Optional[str] = None,
+ thinlayer: BaseThinLayer | None = None,
+ out: str | None = None,
img_format: str = "png",
dpi: int = 300,
**kwargs,
-) -> Tuple[Figure, Union[Axes, List[Axes]]]:
+) -> tuple[Figure, Axes | list[Axes]]:
"""
Creates publication-quality plots of measurement data from an EnzymeML document.
@@ -146,7 +145,7 @@ def _plot_measurement(
ax: Axes,
enzmldoc: v2.EnzymeMLDocument,
measurement: v2.Measurement,
- thinlayer: Optional[BaseThinLayer],
+ thinlayer: BaseThinLayer | None,
marker_size: int,
marker_style: str,
**kwargs,
@@ -205,7 +204,7 @@ def _plot_species(
ax: Axes,
species: v2.MeasurementData,
pred: SimResult,
- time: List[float],
+ time: list[float],
marker_size: int,
marker_style: str,
**kwargs,
@@ -264,7 +263,7 @@ def _plot_species(
def _configure_plot(
- ax: Axes, measurement: v2.Measurement, dataytypes: List[str]
+ ax: Axes, measurement: v2.Measurement, dataytypes: list[str]
) -> None:
"""
Configure plot appearance with titles, labels, and styling.
@@ -305,7 +304,7 @@ def _get_fit(
enzmldoc: v2.EnzymeMLDocument,
measurement: v2.Measurement,
thinlayer: BaseThinLayer,
-) -> Tuple[SimResult, Time]:
+) -> tuple[SimResult, Time]:
"""
Get model predictions for a measurement using a thinlayer.
@@ -350,7 +349,7 @@ def _get_fit(
)
-def _collect_dataytypes(measurement_data: List[v2.MeasurementData]) -> List[str]:
+def _collect_dataytypes(measurement_data: list[v2.MeasurementData]) -> list[str]:
"""
Extract and format unique measurement data types.
diff --git a/pyenzyme/pretty.py b/pyenzyme/pretty.py
index ff6e98af..4670b415 100644
--- a/pyenzyme/pretty.py
+++ b/pyenzyme/pretty.py
@@ -1,8 +1,9 @@
from typing import TYPE_CHECKING
+
+from rich.columns import Columns
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
-from rich.columns import Columns
if TYPE_CHECKING:
from pyenzyme.versions.v2 import EnzymeMLDocument
diff --git a/pyenzyme/sbml/__init__.py b/pyenzyme/sbml/__init__.py
index ab163b1d..0c769fec 100644
--- a/pyenzyme/sbml/__init__.py
+++ b/pyenzyme/sbml/__init__.py
@@ -3,8 +3,8 @@
from .serializer import to_sbml
__all__ = [
- "to_sbml",
- "read_sbml",
"create_sbml_omex",
+ "read_sbml",
"read_sbml_omex",
+ "to_sbml",
]
diff --git a/pyenzyme/sbml/ldutils.py b/pyenzyme/sbml/ldutils.py
index e61a54a7..12cbba42 100644
--- a/pyenzyme/sbml/ldutils.py
+++ b/pyenzyme/sbml/ldutils.py
@@ -1,7 +1,8 @@
-import rdflib
import json
import xml.etree.ElementTree as ET
+import rdflib
+
def parse_sbml_rdf_annotation(sbml_obj, enzml_obj):
"""
diff --git a/pyenzyme/sbml/parser.py b/pyenzyme/sbml/parser.py
index 592c5f23..d194dc53 100644
--- a/pyenzyme/sbml/parser.py
+++ b/pyenzyme/sbml/parser.py
@@ -10,14 +10,13 @@
from loguru import logger
import pyenzyme as pe
-
from pyenzyme import xmlutils
from pyenzyme.logging import add_logger
from . import read_sbml_omex
from .ldutils import parse_sbml_rdf_annotation
-from .versions.handler import VersionHandler, SupportedVersions
from .utils import _get_unit
+from .versions.handler import SupportedVersions, VersionHandler
def read_sbml(cls, path: Path | str):
diff --git a/pyenzyme/sbml/serializer.py b/pyenzyme/sbml/serializer.py
index 67f47e69..20e8327f 100644
--- a/pyenzyme/sbml/serializer.py
+++ b/pyenzyme/sbml/serializer.py
@@ -1,8 +1,8 @@
from __future__ import annotations
+from collections.abc import Callable
from copy import deepcopy
from pathlib import Path
-from typing import Callable, List
import libsbml
import pandas as pd
@@ -629,7 +629,7 @@ def _validate_sbml(sbmldoc: libsbml.SBMLDocument) -> None:
)
-def _assign_ids_to_units(doc_units: List[UnitDefinition]) -> List[UnitDefinition]:
+def _assign_ids_to_units(doc_units: list[UnitDefinition]) -> list[UnitDefinition]:
"""
Assign unique IDs to units.
diff --git a/pyenzyme/sbml/versions/__init__.py b/pyenzyme/sbml/versions/__init__.py
index fe805738..5f4c36e3 100644
--- a/pyenzyme/sbml/versions/__init__.py
+++ b/pyenzyme/sbml/versions/__init__.py
@@ -1,15 +1,15 @@
from .v2 import (
+ ComplexAnnot,
DataAnnot,
- SpeciesDataAnnot,
- ProteinAnnot,
ParameterAnnot,
- ComplexAnnot,
+ ProteinAnnot,
+ SpeciesDataAnnot,
)
__all__ = [
+ "ComplexAnnot",
"DataAnnot",
- "SpeciesDataAnnot",
- "ProteinAnnot",
"ParameterAnnot",
- "ComplexAnnot",
+ "ProteinAnnot",
+ "SpeciesDataAnnot",
]
diff --git a/pyenzyme/tabular.py b/pyenzyme/tabular.py
index 2d9ff46d..8d98855f 100644
--- a/pyenzyme/tabular.py
+++ b/pyenzyme/tabular.py
@@ -3,13 +3,12 @@
import pathlib as pl
import pandas as pd
-
from mdmodels.units.unit_definition import UnitDefinition
from .versions.v2 import (
DataTypes,
- Measurement,
EnzymeMLDocument,
+ Measurement,
MeasurementData,
)
diff --git a/pyenzyme/thinlayers/__init__.py b/pyenzyme/thinlayers/__init__.py
index c8b79104..1a0d02f6 100644
--- a/pyenzyme/thinlayers/__init__.py
+++ b/pyenzyme/thinlayers/__init__.py
@@ -1,5 +1,6 @@
from .base import BaseThinLayer
+
def _get_pysces():
global ThinLayerPysces
try:
@@ -30,4 +31,4 @@ def __getattr__(name):
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
-__all__ = ["BaseThinLayer", "ThinLayerPysces", "ThinLayerCopasi"]
+__all__ = ["BaseThinLayer", "ThinLayerCopasi", "ThinLayerPysces"]
diff --git a/pyenzyme/thinlayers/base.py b/pyenzyme/thinlayers/base.py
index 9eb63464..375a7ed5 100644
--- a/pyenzyme/thinlayers/base.py
+++ b/pyenzyme/thinlayers/base.py
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
from functools import cached_property
-from typing import Dict, List, Optional, Set, Tuple, TypeAlias
+from typing import TypeAlias
import pandas as pd
from sympy import Symbol, sympify
@@ -10,9 +10,9 @@
# Type aliases for usage across the thinlayers
# Easier to read and understand than using type hints
-InitCondDict: TypeAlias = Dict[str, float]
-SimResult: TypeAlias = Dict[str, List[float]]
-Time: TypeAlias = List[float]
+InitCondDict: TypeAlias = dict[str, float]
+SimResult: TypeAlias = dict[str, list[float]]
+Time: TypeAlias = list[float]
class BaseThinLayer(ABC):
@@ -30,13 +30,13 @@ class BaseThinLayer(ABC):
"""
enzmldoc: v2.EnzymeMLDocument
- measurement_ids: List[str]
+ measurement_ids: list[str]
exclude_unmodeled_species: bool = True
def __init__(
self,
enzmldoc: v2.EnzymeMLDocument,
- measurement_ids: Optional[List[str]] = None,
+ measurement_ids: list[str] | None = None,
df_per_measurement: bool = False,
exclude_unmodeled_species: bool = True,
):
@@ -149,7 +149,7 @@ def _remove_unmodeled_species(enzmldoc: v2.EnzymeMLDocument) -> v2.EnzymeMLDocum
return enzmldoc
@staticmethod
- def _get_all_species(enzmldoc: v2.EnzymeMLDocument) -> Set[str]:
+ def _get_all_species(enzmldoc: v2.EnzymeMLDocument) -> set[str]:
"""
Gets all species from the EnzymeML document.
"""
@@ -168,7 +168,7 @@ def integrate(
t0: float,
t1: float,
nsteps: int = 100,
- ) -> Tuple[SimResult, Time]:
+ ) -> tuple[SimResult, Time]:
"""
Integrates the model from t0 to t1 with the given initial conditions.
diff --git a/pyenzyme/thinlayers/basico.py b/pyenzyme/thinlayers/basico.py
index dd011a0e..20517dc4 100644
--- a/pyenzyme/thinlayers/basico.py
+++ b/pyenzyme/thinlayers/basico.py
@@ -6,15 +6,14 @@
from __future__ import annotations
+import os
from dataclasses import dataclass
from pathlib import Path
+
import numpy as np
import pandas as pd
-import os
-
-from typing import Dict, List, Optional, Tuple
-from pyenzyme.thinlayers.base import BaseThinLayer, SimResult, Time, InitCondDict
+from pyenzyme.thinlayers.base import BaseThinLayer, InitCondDict, SimResult, Time
from pyenzyme.versions import v2
try:
@@ -54,7 +53,7 @@ def __init__(
self,
enzmldoc: v2.EnzymeMLDocument,
model_dir: Path | str = "./copasi_models",
- measurement_ids: Optional[List[str]] = None,
+ measurement_ids: list[str] | None = None,
):
"""
Initialize the ThinLayerCopasi instance.
@@ -113,7 +112,7 @@ def integrate(
t0: float,
t1: float,
nsteps: int = 100,
- ) -> Tuple[SimResult, Time]:
+ ) -> tuple[SimResult, Time]:
"""
Integrates the model from t0 to t1 with the given initial conditions.
@@ -397,7 +396,7 @@ def _create_sbml_file(self, model_dir: str) -> str:
def _simulate_condition(
self, init_concs: InitMap
- ) -> Tuple[List[np.ndarray], List[str]]:
+ ) -> tuple[list[np.ndarray], list[str]]:
"""
Simulates a single experimental condition.
@@ -438,11 +437,11 @@ class InitMap:
species (Dict[str, float]): Dictionary mapping species IDs to initial concentrations.
"""
- time: List[float]
- species: Dict[str, float]
+ time: list[float]
+ species: dict[str, float]
@classmethod
- def from_measurement(cls, meas: v2.Measurement, df: pd.DataFrame) -> "InitMap":
+ def from_measurement(cls, meas: v2.Measurement, df: pd.DataFrame) -> InitMap:
"""
Create an InitMap instance from a measurement and its dataframe.
diff --git a/pyenzyme/thinlayers/psyces.py b/pyenzyme/thinlayers/psyces.py
index caacf79a..c69e6e4e 100644
--- a/pyenzyme/thinlayers/psyces.py
+++ b/pyenzyme/thinlayers/psyces.py
@@ -11,7 +11,6 @@
import os
from dataclasses import dataclass
from pathlib import Path
-from typing import Dict, List, Optional, Tuple
import dill
import numpy as np
@@ -63,7 +62,7 @@ def __init__(
self,
enzmldoc: v2.EnzymeMLDocument,
model_dir: Path | str = "./pysces_models",
- measurement_ids: Optional[List[str]] = None,
+ measurement_ids: list[str] | None = None,
):
"""
Initialize the ThinLayerPysces instance.
@@ -128,7 +127,7 @@ def integrate(
t0: float,
t1: float,
nsteps: int = 100,
- ) -> Tuple[SimResult, Time]:
+ ) -> tuple[SimResult, Time]:
"""
Integrates the model from t0 to t1 with the given initial conditions.
@@ -469,7 +468,7 @@ def _simulate_experiment(self, parameters):
def _simulate_condition(
self, init_concs: InitMap
- ) -> Tuple[List[np.ndarray], List[str]]:
+ ) -> tuple[list[np.ndarray], list[str]]:
"""
Simulates a single experimental condition.
@@ -502,11 +501,11 @@ class InitMap:
species (Dict[str, float]): Dictionary mapping species IDs to initial concentrations.
"""
- time: List[float]
- species: Dict[str, float]
+ time: list[float]
+ species: dict[str, float]
@classmethod
- def from_measurement(cls, meas: v2.Measurement, df: pd.DataFrame) -> "InitMap":
+ def from_measurement(cls, meas: v2.Measurement, df: pd.DataFrame) -> InitMap:
"""
Create an InitMap instance from a measurement and its dataframe.
diff --git a/pyenzyme/versions/io.py b/pyenzyme/versions/io.py
index d97abcb1..3ab84aee 100644
--- a/pyenzyme/versions/io.py
+++ b/pyenzyme/versions/io.py
@@ -1,6 +1,5 @@
import json
from pathlib import Path
-from typing import Optional
import pandas as pd
import rich
@@ -24,7 +23,7 @@ class EnzymeMLHandler:
"""
@classmethod
- def read_enzymeml(cls, path: str) -> v2.EnzymeMLDocument: # noqa: F405
+ def read_enzymeml(cls, path: str) -> v2.EnzymeMLDocument:
"""Read an EnzymeML document from a file.
Attempts to read the document using different version parsers until successful.
@@ -47,7 +46,7 @@ def read_enzymeml(cls, path: str) -> v2.EnzymeMLDocument: # noqa: F405
continue
elif version == "v2":
try:
- with open(path, "r") as f:
+ with open(path) as f:
data = json.load(f)
return v2.EnzymeMLDocument.model_validate(data)
@@ -58,7 +57,7 @@ def read_enzymeml(cls, path: str) -> v2.EnzymeMLDocument: # noqa: F405
raise ValueError(f"Invalid EnzymeML version: {path}") from error
@classmethod
- def read_enzymeml_from_string(cls, data: str) -> v2.EnzymeMLDocument: # noqa: F405
+ def read_enzymeml_from_string(cls, data: str) -> v2.EnzymeMLDocument:
"""Read an EnzymeML document from a string.
Attempts to read the document using different version parsers until successful.
@@ -87,7 +86,7 @@ def from_dataframe(
df: pd.DataFrame,
data_unit: str,
time_unit: str,
- ) -> list[v2.Measurement]: # noqa: F405
+ ) -> list[v2.Measurement]:
"""Parse a pandas DataFrame into a list of measurements.
This function expects the DataFrame to have the following structure:
@@ -120,7 +119,7 @@ def write_enzymeml(
cls,
enzmldoc: v2.EnzymeMLDocument,
path: Path | str | None = None,
- ) -> Optional[str]: # noqa: F405
+ ) -> str | None:
"""Write an EnzymeML document to a file or return as a string.
Args:
@@ -154,7 +153,7 @@ def to_sbml(
enzmldoc: v2.EnzymeMLDocument,
path: Path | str | None = None,
verbose: bool = False,
- ) -> tuple[str, pd.DataFrame | None]: # noqa: F405
+ ) -> tuple[str, pd.DataFrame | None]:
"""Convert an EnzymeML document to SBML format and write it to a file.
The systems biology markup language (SBML) is a machine-readable format for
@@ -186,7 +185,7 @@ def to_petab(
cls,
enzmldoc: v2.EnzymeMLDocument,
path: Path | str,
- ) -> PEtab: # noqa: F405
+ ) -> PEtab:
"""
Convert an EnzymeML document to a PEtab parameter estimation problem and write to file.
@@ -226,7 +225,7 @@ def to_petab(
def from_sbml(
cls,
path: Path | str,
- ) -> v2.EnzymeMLDocument: # noqa: F405
+ ) -> v2.EnzymeMLDocument:
"""
Read an SBML file and initialize an EnzymeML document.
@@ -249,7 +248,7 @@ def to_pandas(
cls,
enzmldoc: v2.EnzymeMLDocument,
per_measurement: bool = False,
- ) -> pd.DataFrame | dict[str, pd.DataFrame]: # noqa: F405
+ ) -> pd.DataFrame | dict[str, pd.DataFrame]:
"""Convert an EnzymeML document to a pandas DataFrame.
The resulting DataFrame contains the following columns:
@@ -285,7 +284,7 @@ def from_csv(
time_unit: str,
data_type: v2.DataTypes = v2.DataTypes.CONCENTRATION,
sep: str = "\t",
- ) -> list[v2.Measurement]: # noqa: F405
+ ) -> list[v2.Measurement]:
"""Reads a CSV file from the specified path into a measurement.
This function expects the CSV file to have the following structure:
@@ -322,7 +321,7 @@ def from_excel(
data_unit: str,
time_unit: str,
data_type: v2.DataTypes = v2.DataTypes.CONCENTRATION,
- ) -> list[v2.Measurement]: # noqa: F405
+ ) -> list[v2.Measurement]:
"""Reads an Excel file from the specified path into Measurement objects.
This function expects the Excel file to have the following structure:
diff --git a/pyenzyme/widgets.py b/pyenzyme/widgets.py
index accb45af..587a98fa 100644
--- a/pyenzyme/widgets.py
+++ b/pyenzyme/widgets.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Any, get_origin, get_args
+from typing import TYPE_CHECKING, Any, get_args, get_origin
if TYPE_CHECKING:
from pyenzyme.versions.v2 import EnzymeMLDocument
diff --git a/pyproject.toml b/pyproject.toml
index b57b4599..41c6306f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -47,6 +47,14 @@ copasi = ["copasi-basico>=0.85"]
[tool.pytest.ini_options]
markers = ["remote: tests that require network access to external services"]
+[tool.ruff]
+# Generated by md-models ("do not edit") and the legacy v1 package.
+extend-exclude = ["pyenzyme/versions/v2.py", "pyenzyme/v1"]
+
+[tool.ruff.lint]
+# Ruff <0.16 defaults plus the rule families ruff can autofix.
+select = ["E4", "E7", "E9", "F", "I", "UP", "RUF010", "RUF022", "RUF100"]
+
[tool.uv]
[build-system]
diff --git a/tests/conftest.py b/tests/conftest.py
index 34f3d1b8..92c7e160 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,4 +1,5 @@
import pytest
+
import pyenzyme as pe
diff --git a/tests/integration/test_basico.py b/tests/integration/test_basico.py
index f6c83aa5..be13e2c0 100644
--- a/tests/integration/test_basico.py
+++ b/tests/integration/test_basico.py
@@ -1,6 +1,7 @@
import tempfile
import pytest
+
import pyenzyme as pe
from pyenzyme.thinlayers.basico import ThinLayerCopasi
from pyenzyme.versions import v2
diff --git a/tests/integration/test_composer.py b/tests/integration/test_composer.py
index 96479f16..a5c82f8e 100644
--- a/tests/integration/test_composer.py
+++ b/tests/integration/test_composer.py
@@ -1,4 +1,5 @@
import pytest
+
import pyenzyme as pe
from pyenzyme.tools import to_dict_wo_json_ld
diff --git a/tests/integration/test_petab.py b/tests/integration/test_petab.py
index bd5b4994..ae1a52d0 100644
--- a/tests/integration/test_petab.py
+++ b/tests/integration/test_petab.py
@@ -1,5 +1,6 @@
import re
import tempfile
+
import pytest
import pyenzyme as pe
diff --git a/tests/integration/test_psyces.py b/tests/integration/test_psyces.py
index 1a98c4b0..069303c0 100644
--- a/tests/integration/test_psyces.py
+++ b/tests/integration/test_psyces.py
@@ -1,6 +1,7 @@
import tempfile
import pytest
+
import pyenzyme as pe
from pyenzyme.thinlayers.psyces import ThinLayerPysces
from pyenzyme.versions import v2
diff --git a/tests/integration/test_suite.py b/tests/integration/test_suite.py
index 46dadfa8..4ea3f688 100644
--- a/tests/integration/test_suite.py
+++ b/tests/integration/test_suite.py
@@ -1,8 +1,9 @@
import json
-import pyenzyme as pe
-from pyenzyme.suite import EnzymeMLSuite
+
from pytest_httpx import HTTPXMock
+import pyenzyme as pe
+from pyenzyme.suite import EnzymeMLSuite
from pyenzyme.tools import to_dict_wo_json_ld
from pyenzyme.versions import v2
@@ -10,7 +11,7 @@
class TestSuite:
def test_fetch_current(self, httpx_mock: HTTPXMock):
# Arrange
- with open("tests/fixtures/sbml/ode_example_enzml.json", "r") as f:
+ with open("tests/fixtures/sbml/ode_example_enzml.json") as f:
data = json.load(f)
httpx_mock.add_response(json={"data": {"content": data}})
@@ -24,7 +25,7 @@ def test_fetch_current(self, httpx_mock: HTTPXMock):
def test_update_current(self, httpx_mock: HTTPXMock):
# Arrange
- with open("tests/fixtures/sbml/ode_example_enzml.json", "r") as f:
+ with open("tests/fixtures/sbml/ode_example_enzml.json") as f:
data = pe.read_enzymeml_from_string(json.load(f))
httpx_mock.add_response(
match_json=json.loads(data.model_dump_json()),
diff --git a/tests/unit/test_equations.py b/tests/unit/test_equations.py
index a43df460..8e9d3337 100644
--- a/tests/unit/test_equations.py
+++ b/tests/unit/test_equations.py
@@ -1,8 +1,8 @@
import pytest
+from pyenzyme import EnzymeMLDocument, EquationType
from pyenzyme.equations.chem import build_reaction, build_reactions
from pyenzyme.equations.math import build_equation, build_equations
-from pyenzyme import EquationType, EnzymeMLDocument
class TestMathEquations:
diff --git a/tests/unit/test_sbml_annot_v1.py b/tests/unit/test_sbml_annot_v1.py
index ff6ee602..4dbf553c 100644
--- a/tests/unit/test_sbml_annot_v1.py
+++ b/tests/unit/test_sbml_annot_v1.py
@@ -1,11 +1,11 @@
import pandas as pd
-
from mdmodels.units.converter import convert_unit
+
from pyenzyme.sbml.versions.v1 import (
- DataAnnot,
- ReactantAnnot,
ComplexAnnot,
+ DataAnnot,
ProteinAnnot,
+ ReactantAnnot,
)
diff --git a/tests/unit/test_sbml_annot_v2.py b/tests/unit/test_sbml_annot_v2.py
index 3a132f05..73f7187a 100644
--- a/tests/unit/test_sbml_annot_v2.py
+++ b/tests/unit/test_sbml_annot_v2.py
@@ -1,15 +1,15 @@
from pyenzyme.sbml.versions.v2 import (
+ ConditionsAnnot,
DataAnnot,
MeasurementAnnot,
- ConditionsAnnot,
- SpeciesDataAnnot,
- TemperatureAnnot,
+ ParameterAnnot,
PHAnnot,
- SmallMoleculeAnnot,
ProteinAnnot,
- ParameterAnnot,
- VariablesAnnot,
+ SmallMoleculeAnnot,
+ SpeciesDataAnnot,
+ TemperatureAnnot,
VariableAnnot,
+ VariablesAnnot,
)