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, )