Source code for cobra.manipulation.modify

"""Provide functions to modify model components."""

from ast import NodeTransformer
from functools import partial
from itertools import chain
from typing import TYPE_CHECKING, Dict

from cobra.util import get_context


if TYPE_CHECKING:
    from cobra import Gene, Model

# Set of tuples of operators and their corresponding textual form
[docs]_renames = ( (".", "_DOT_"), ("(", "_LPAREN_"), (")", "_RPAREN_"), ("-", "__"), ("[", "_LSQBKT"), ("]", "_RSQBKT"), (",", "_COMMA_"), (":", "_COLON_"), (">", "_GT_"), ("<", "_LT"), ("/", "_FLASH"), ("\\", "_BSLASH"), ("+", "_PLUS_"), ("=", "_EQ_"), (" ", "_SPACE_"), ("'", "_SQUOT_"), ('"', "_DQUOT_"),
)
[docs]def _escape_str_id(id_str: str) -> str: """Make a single string ID SBML compliant. Parameters ---------- id_str: str The ID string to operate on. Returns ------- str The SBML compliant ID string. """ for c in ("'", '"'): if id_str.startswith(c) and id_str.endswith(c) and id_str.count(c) == 2: id_str = id_str.strip(c) for char, escaped_char in _renames: id_str = id_str.replace(char, escaped_char) return id_str
[docs]class _GeneEscaper(NodeTransformer): """Class to represent a gene ID escaper."""
[docs] def visit_Name(self, node: "Gene") -> "Gene": """Escape string ID. Parameters ---------- node: cobra.Gene The gene object to work on. Returns ------- cobra.Gene The gene object whose ID has been escaped. """ node.id = _escape_str_id(node.id) return node
[docs]def escape_ID(model: "Model") -> None: """Make all model component object IDs SBML compliant. Parameters ---------- model: cobra.Model The model to operate on. """ for x in chain([model], model.metabolites, model.reactions, model.genes): x.id = _escape_str_id(x.id) gene_renamer = _GeneEscaper() for rxn in model.reactions: if rxn.gpr is not None: gene_renamer.visit(rxn.gpr) model.repair()
[docs]class _Renamer(NodeTransformer): """ Class to represent a gene renamer. Parameters ---------- rename_dict: dict of {str: str} The dictionary having keys as old gene names and value as new gene names. """ def __init__(self, rename_dict: Dict[str, str], **kwargs) -> None: """Initialize a new object. Other Parameters ---------------- **kwargs: Further keyword arguments are passed on to the parent class. """ super().__init__(**kwargs) self.rename_dict = rename_dict # That's not right
[docs] def visit_Name(self, node: "Gene") -> "Gene": """Rename a gene. Parameters ---------- node: cobra.Gene The gene to rename. Returns ------- cobra.Gene The renamed gene object. """ node.id = self.rename_dict.get(node.id, node.id) return node
[docs]def rename_genes(model: "Model", rename_dict: Dict[str, str]) -> None: """Rename genes in a model from the `rename_dict`. Parameters ---------- model: cobra.Model The model to operate on. rename_dict: dict of {str: str} The dictionary having keys as old gene names and value as new gene names. """ recompute_reactions = set() # need to recompute related genes remove_genes = set() context = get_context(model) # Needs to be added first since the history is executed from the tail and # this has to be run last to repair the Gene <-> Reaction mapping if context: for rxn in model.reactions: context(partial(rxn.update_genes_from_gpr)) for old_name, new_name in rename_dict.items(): # undefined if there a value matches a different key try: gene_index = model.genes.index(old_name) except ValueError: continue new_gene_present = new_name in model.genes if new_gene_present: old_gene = model.genes.get_by_id(old_name) # Added in case not renaming some genes: if old_gene is not model.genes.get_by_id(new_name): remove_genes.add(old_gene) recompute_reactions.update(old_gene.reactions) else: # rename old gene to new gene gene = model.genes[gene_index] gene.id = new_name model.genes._generate_index() recompute_reactions.update(gene.reactions) if context: context(model.genes._generate_index) context(partial(setattr, gene, "id", old_name)) gene_renamer = _Renamer(rename_dict) for rxn in recompute_reactions: if rxn.gpr is not None: old_gpr = rxn.gpr.copy() gene_renamer.visit(rxn.gpr) if context: context(partial(setattr, rxn, "_gpr", old_gpr)) model.repair() for i in remove_genes: model.genes.remove(i) i._model = None if context: context(partial(model.genes.add, i)) context(partial(setattr, i, "_model", model))