From eca2d0d1e6fba316cc772ff37216213b0edf50e1 Mon Sep 17 00:00:00 2001 From: Elarwei Date: Wed, 24 Jun 2026 22:51:30 +0800 Subject: [PATCH 1/5] feat(reactome): add gget reactome module to query the Reactome pathway database (#114) New module `gget reactome` (Python API + CLI) querying the Reactome ContentService REST API (https://reactome.org/). Supports three query types via the `resource` argument: - pathways (default): the Reactome pathways an identifier (e.g. a UniProt accession) participates in - search: full-text search of the Reactome knowledgebase - entity: details for a Reactome stable ID Returns a pandas DataFrame (or JSON with json=True / --csv on the CLI). Reuses the shared http_json helper for retries/error handling, strips HTML highlight tags from search results, and maps Reactome 404s to empty results (pathways/search) or a clear ValueError (entity). Adds unit tests (offline argument validation via fixture + release-robust network smoke tests that skip when Reactome is unreachable), docs page docs/src/en/reactome.md, a SUMMARY.md entry, and an updates.md note. Resolves #114. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/src/SUMMARY.md | 1 + docs/src/en/reactome.md | 111 ++++++++++++++ docs/src/en/updates.md | 1 + gget/__init__.py | 1 + gget/gget_reactome.py | 239 ++++++++++++++++++++++++++++++ gget/main.py | 116 +++++++++++++++ tests/fixtures/test_reactome.json | 19 +++ tests/test_reactome.py | 77 ++++++++++ 8 files changed, 565 insertions(+) create mode 100644 docs/src/en/reactome.md create mode 100644 gget/gget_reactome.py create mode 100644 tests/fixtures/test_reactome.json create mode 100644 tests/test_reactome.py diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b112fd0f2..b34faa745 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -28,6 +28,7 @@ * [gget mutate](en/mutate.md) * [gget opentargets](en/opentargets.md) * [gget pdb](en/pdb.md) +* [gget reactome](en/reactome.md) * [gget ref](en/ref.md) * [gget search](en/search.md) * [gget setup](en/setup.md) diff --git a/docs/src/en/reactome.md b/docs/src/en/reactome.md new file mode 100644 index 000000000..2ae0c50e2 --- /dev/null +++ b/docs/src/en/reactome.md @@ -0,0 +1,111 @@ +[ View page source on GitHub ](https://github.com/scverse/gget/blob/main/docs/src/en/reactome.md) + +> Python arguments are equivalent to long-option arguments (`--arg`), unless otherwise specified. Flags are True/False arguments in Python. The manual for any gget tool can be called from the command-line using the `-h` `--help` flag. +# gget reactome 🔬 +Query the [Reactome](https://reactome.org/) pathway knowledgebase using its [ContentService REST API](https://reactome.org/dev/content-service). +Return format: JSON/CSV (command-line) or data frame (Python). + +`gget reactome` supports three kinds of queries, selected with the `resource` argument: +- `pathways` (default): return the Reactome pathways a given identifier (e.g. a UniProt accession) participates in. +- `search`: full-text search of the Reactome knowledgebase (pathways, reactions, physical entities, ...). +- `entity`: return details for a Reactome stable ID (e.g. `R-HSA-6804754`). + +**Positional argument** +`query` +Identifier or search term to query. Its meaning depends on `resource`: +- `resource="pathways"`: an identifier, e.g. UniProt accession `P04637`. +- `resource="search"`: a free-text search term, e.g. `TP53`. +- `resource="entity"`: a Reactome stable ID, e.g. `R-HSA-6804754`. + +**Optional arguments** +`-r` `--resource` +Type of query to perform. Options: `pathways` (default), `search`, `entity`. + +`-s` `--source` +Identifier resource/database for `resource="pathways"`, e.g. `UniProt` (default), `Ensembl`, `ChEBI`, `NCBI`. + +`-sp` `--species` +Restrict results to a species, as a name (e.g. `Homo sapiens`) or NCBI taxonomy ID (e.g. `9606`). Applies to `resource="pathways"` and `resource="search"`. Default: None (no species filter). + +`-t` `--types` +Restrict `resource="search"` results to one or more entry types, e.g. `Pathway`, `Reaction`, `Protein`. Default: None (all types). + +`-o` `--out` +Path to the file the results will be saved in, e.g. path/to/directory/results.json. Default: Standard out. + +**Flags** +`-csv` `--csv` +Command-line only. Returns the output in CSV format, instead of JSON format. +Python: Use `json=True` to return output in JSON format. + +`-q` `--quiet` +Command-line only. Prevents progress information from being displayed. +Python: Use `verbose=False` to prevent progress information from being displayed. + + +### Examples + +**Get the Reactome pathways a protein participates in** + +```bash +gget reactome P04637 --species "Homo sapiens" +``` + +```python +import gget +gget.reactome("P04637", species="Homo sapiens") +``` + +→ Returns the Reactome pathways the UniProt protein P04637 (human TP53) participates in. + +| stable_id | name | species | schema_class | in_disease | +|---------------|------------------------------------------------------|--------------|--------------|------------| +| R-HSA-111448 | Activation of NOXA and translocation to mitochondria | Homo sapiens | Pathway | False | +| R-HSA-139915 | Activation of PUMA and translocation to mitochondria | Homo sapiens | Pathway | False | +| ... | ... | ... | ... | ... | + +

+ +**Search the Reactome knowledgebase** + +```bash +gget reactome TP53 -r search -t Pathway -sp "Homo sapiens" +``` +```python +import gget +gget.reactome("TP53", resource="search", types="Pathway", species="Homo sapiens") +``` + +→ Returns Reactome pathways matching the search term "TP53". + +| stable_id | name | type | species | reactome_id | +|---------------|-------------------------------|---------|--------------|---------------| +| R-HSA-6804754 | Regulation of TP53 Expression | Pathway | Homo sapiens | R-HSA-6804754 | +| R-HSA-5633007 | Regulation of TP53 Activity | Pathway | Homo sapiens | R-HSA-5633007 | +| ... | ... | ... | ... | ... | + +

+ +**Get details for a Reactome entry by stable ID** + +```bash +gget reactome R-HSA-6804754 -r entity +``` +```python +import gget +gget.reactome("R-HSA-6804754", resource="entity") +``` + +→ Returns details for the Reactome entry R-HSA-6804754. + +| stable_id | name | schema_class | species | in_disease | summation | +|---------------|-------------------------------|--------------|--------------|------------|------------------| +| R-HSA-6804754 | Regulation of TP53 Expression | Pathway | Homo sapiens | False | Transcription... | + + +# References +If you use `gget reactome` in a publication, please cite the following articles: + +- Luebbert, L., & Pachter, L. (2023). Efficient querying of genomic reference databases with gget. Bioinformatics. [https://doi.org/10.1093/bioinformatics/btac836](https://doi.org/10.1093/bioinformatics/btac836) + +- Marc Gillespie, Bijay Jassal, Ralf Stephan, Marija Milacic, Karen Rothfels, Andrea Senff-Ribeiro, Johannes Griss, Cristoffer Sevilla, Lisa Matthews, Chuqiao Gong, Chuan Deng, Thawfeek Varusai, Eliot Ragueneau, Yusra Haider, Bruce May, Veronica Shamovsky, Joel Weiser, Timothy Brunson, Nasim Sanati, Liam Beckman, Xiang Shao, Antonio Fabregat, Konstantinos Sidiropoulos, Julieth Murillo, Guilherme Viteri, Justin Cook, Solomon Shorser, Gary Bader, Emek Demir, Chris Sander, Robin Haw, Guanming Wu, Lincoln Stein, Henning Hermjakob, Peter D'Eustachio (2022). The reactome pathway knowledgebase 2022. Nucleic Acids Research, Volume 50, Issue D1, 7 January 2022, Pages D687–D692, [https://doi.org/10.1093/nar/gkab1028](https://doi.org/10.1093/nar/gkab1028) diff --git a/docs/src/en/updates.md b/docs/src/en/updates.md index 89be22281..8aa0c5850 100644 --- a/docs/src/en/updates.md +++ b/docs/src/en/updates.md @@ -5,6 +5,7 @@ #### *gget* officially became part of [*scverse*](https://scverse.org/) on June 9, 2026. 🥳🥳🥳 **Version ≥ 0.30.9** (XXX XX, 2026): +- [`gget reactome`](reactome.md): **New module** to query the [Reactome](https://reactome.org/) pathway knowledgebase via its ContentService REST API. Supports three query types via the `resource` argument: `pathways` (the Reactome pathways an identifier such as a UniProt accession participates in), `search` (full-text search of the knowledgebase), and `entity` (details for a Reactome stable ID). Resolves [issue 114](https://github.com/scverse/gget/issues/114). **Version ≥ 0.30.8** (Jun 28, 2026): - [`gget g2p`](g2p.md): Either `gene` or `--uniprot_id` is now sufficient — whichever is missing is resolved via UniProt and cached. Gene→UniProt picks the canonical reviewed human Swiss-Prot entry; the resolution and its limitations are logged. The canonical pair is **always** prepended to the result as `gene_name` / `uniprot_id` columns (and stored on `df.attrs`), so the output schema is invariant regardless of input mode. Existing call sites continue to work. diff --git a/gget/__init__.py b/gget/__init__.py index f56cbcddc..1ed240a05 100644 --- a/gget/__init__.py +++ b/gget/__init__.py @@ -22,6 +22,7 @@ from .gget_mutate import mutate from .gget_opentargets import opentargets from .gget_pdb import pdb +from .gget_reactome import reactome from .gget_ref import ref from .gget_search import search from .gget_seq import seq diff --git a/gget/gget_reactome.py b/gget/gget_reactome.py new file mode 100644 index 000000000..9b8192b46 --- /dev/null +++ b/gget/gget_reactome.py @@ -0,0 +1,239 @@ +from __future__ import annotations + +import json as json_ +import re +from importlib.metadata import PackageNotFoundError, version +from typing import Any + +import pandas as pd + +from .utils import http_json, set_up_logger + +logger = set_up_logger() + +try: + _GGET_VERSION = version("gget") +except PackageNotFoundError: + _GGET_VERSION = "unknown" + +# Reactome ContentService REST API (https://reactome.org/dev/content-service) +REACTOME_CONTENT_API = "https://reactome.org/ContentService" + +_REACTOME_HEADERS = { + "User-Agent": f"gget/{_GGET_VERSION} (+https://github.com/scverse/gget)", + "Accept": "application/json", +} + +# Supported values for the 'resource' argument +REACTOME_RESOURCES = ("pathways", "search", "entity") + +# Regex to strip the ... tags Reactome wraps around +# matched search terms. +_HTML_TAG_RE = re.compile(r"<[^>]+>") + + +def _strip_html(text: Any) -> Any: + """Remove HTML highlight tags returned by the Reactome search endpoint.""" + if isinstance(text, str): + return _HTML_TAG_RE.sub("", text) + return text + + +def _is_http_status(error: Exception, status: int) -> bool: + """True if the RuntimeError raised by http_json corresponds to the given HTTP status.""" + return f"HTTP {status}" in str(error) + + +def _reactome_pathways( + query: str, + source: str = "UniProt", + species: str | None = None, + verbose: bool = True, +) -> pd.DataFrame: + """Map an identifier to the Reactome pathways it participates in. + + Uses the ContentService '/data/mapping/{source}/{identifier}/pathways' endpoint. + """ + if verbose: + logger.info(f"Querying Reactome for pathways containing {source} identifier '{query}'.") + + params = {} + if species is not None: + params["species"] = species + + try: + payload = http_json( + "GET", + f"{REACTOME_CONTENT_API}/data/mapping/{source}/{query}/pathways", + context="Reactome ContentService (pathways)", + headers=_REACTOME_HEADERS, + params=params, + ) + except RuntimeError as e: + # Reactome returns 404 when the identifier is not found / has no mapped pathways. + if _is_http_status(e, 404): + logger.warning(f"No Reactome pathways found for {source} identifier '{query}'.") + return pd.DataFrame(columns=["stable_id", "name", "species", "schema_class", "in_disease"]) + raise + + rows = [ + { + "stable_id": entry.get("stId"), + "name": entry.get("displayName"), + "species": entry.get("speciesName"), + "schema_class": entry.get("schemaClass"), + "in_disease": entry.get("isInDisease"), + } + for entry in payload + ] + return pd.DataFrame(rows, columns=["stable_id", "name", "species", "schema_class", "in_disease"]) + + +def _reactome_search( + query: str, + species: str | None = None, + types: str | None = None, + verbose: bool = True, +) -> pd.DataFrame: + """Full-text search of the Reactome knowledgebase. + + Uses the ContentService '/search/query' endpoint and flattens the grouped results. + """ + if verbose: + logger.info(f"Searching Reactome for '{query}'.") + + params: dict[str, Any] = {"query": query, "cluster": "true"} + if species is not None: + params["species"] = species + if types is not None: + params["types"] = types + + try: + payload = http_json( + "GET", + f"{REACTOME_CONTENT_API}/search/query", + context="Reactome ContentService (search)", + headers=_REACTOME_HEADERS, + params=params, + ) + except RuntimeError as e: + # Reactome returns 404 when the search yields no results. + if _is_http_status(e, 404): + logger.warning(f"No Reactome search results found for '{query}'.") + return pd.DataFrame(columns=["stable_id", "name", "type", "species", "reactome_id"]) + raise + + rows = [] + for group in payload.get("results", []): + for entry in group.get("entries", []): + entry_species = entry.get("species") + if isinstance(entry_species, list): + entry_species = entry_species[0] if entry_species else None + rows.append( + { + "stable_id": entry.get("stId"), + "name": _strip_html(entry.get("name")), + "type": entry.get("exactType") or entry.get("type"), + "species": entry_species, + "reactome_id": entry.get("id"), + } + ) + return pd.DataFrame(rows, columns=["stable_id", "name", "type", "species", "reactome_id"]) + + +def _reactome_entity(query: str, verbose: bool = True) -> pd.DataFrame: + """Fetch details for a Reactome entry (pathway, reaction, physical entity, ...) by stable ID. + + Uses the ContentService '/data/query/{id}' endpoint. + """ + if verbose: + logger.info(f"Fetching Reactome entry '{query}'.") + + try: + payload = http_json( + "GET", + f"{REACTOME_CONTENT_API}/data/query/{query}", + context="Reactome ContentService (entity)", + headers=_REACTOME_HEADERS, + ) + except RuntimeError as e: + if _is_http_status(e, 404): + raise ValueError( + f"No Reactome entry found for identifier '{query}'. " + "Provide a valid Reactome stable ID (e.g. 'R-HSA-6804754') or database ID." + ) from e + raise + + name = payload.get("displayName") + if name is None: + names = payload.get("name") + if isinstance(names, list) and names: + name = names[0] + + row = { + "stable_id": payload.get("stId"), + "name": name, + "schema_class": payload.get("schemaClass"), + "species": payload.get("speciesName"), + "in_disease": payload.get("isInDisease"), + "summation": None, + } + summation = payload.get("summation") + if isinstance(summation, list) and summation: + row["summation"] = _strip_html(summation[0].get("text")) + + return pd.DataFrame([row], columns=["stable_id", "name", "schema_class", "species", "in_disease", "summation"]) + + +def reactome( + query: str, + resource: str = "pathways", + source: str = "UniProt", + species: str | None = None, + types: str | None = None, + json: bool = False, + verbose: bool = True, +) -> pd.DataFrame | list[dict[str, Any]]: + """Query the Reactome pathway knowledgebase (https://reactome.org/). + + Args: + - query Identifier or search term to query (str). Its meaning depends on 'resource': + - resource="pathways": an identifier (e.g. UniProt accession 'P04637') whose + Reactome pathways should be returned. + - resource="search": a free-text search term (e.g. 'TP53'). + - resource="entity": a Reactome stable ID (e.g. 'R-HSA-6804754'). + - resource Type of query to perform (str). One of: + - "pathways" (default): pathways the identifier participates in. + - "search": full-text search of the Reactome knowledgebase. + - "entity": details for a Reactome stable ID. + - source Identifier resource/database for resource="pathways" (str), e.g. 'UniProt', + 'Ensembl', 'ChEBI', 'NCBI'. Default: "UniProt". + - species Restrict results to a species (str), as a name (e.g. 'Homo sapiens') or NCBI + taxonomy ID (e.g. '9606'). Applies to resource="pathways" and resource="search". + Default: None (no species filter). + - types Restrict resource="search" results to one or more entry types (str), e.g. + 'Pathway', 'Reaction', 'Protein'. Default: None (all types). + - json If True, return the result as a JSON-serializable list of dicts instead of a + DataFrame (default: False). + - verbose True/False whether to print progress information. Default: True. + + Returns the requested information as a DataFrame (or list of dicts if json=True). + """ + if resource not in REACTOME_RESOURCES: + raise ValueError( + f"Argument 'resource' must be one of {REACTOME_RESOURCES}, not '{resource}'." + ) + + if not isinstance(query, str) or not query.strip(): + raise ValueError("Argument 'query' must be a non-empty string.") + + if resource == "pathways": + df = _reactome_pathways(query, source=source, species=species, verbose=verbose) + elif resource == "search": + df = _reactome_search(query, species=species, types=types, verbose=verbose) + else: # resource == "entity" + df = _reactome_entity(query, verbose=verbose) + + if json: + return json_.loads(df.to_json(orient="records", force_ascii=False)) + return df diff --git a/gget/main.py b/gget/main.py index 7a2944b09..eb8afaa0e 100644 --- a/gget/main.py +++ b/gget/main.py @@ -37,6 +37,7 @@ from .gget_mutate import mutate # noqa: E402 from .gget_opentargets import OPENTARGETS_RESOURCES, opentargets # noqa: E402 from .gget_pdb import pdb # noqa: E402 +from .gget_reactome import reactome # noqa: E402 from .gget_ref import ref # noqa: E402 from .gget_search import search # noqa: E402 from .gget_seq import seq # noqa: E402 @@ -2342,6 +2343,91 @@ def main() -> None: help="Does not print progress information.", ) + ## reactome parser arguments + reactome_desc = "Query the Reactome pathway database (https://reactome.org/)." + parser_reactome = parent_subparsers.add_parser( + "reactome", + parents=[parent], + description=reactome_desc, + help=reactome_desc, + add_help=True, + formatter_class=CustomHelpFormatter, + ) + parser_reactome.add_argument( + "query", + type=str, + help=( + "Identifier or search term to query. Meaning depends on --resource:\n" + " - resource 'pathways': an identifier (e.g. UniProt accession 'P04637').\n" + " - resource 'search': a free-text search term (e.g. 'TP53').\n" + " - resource 'entity': a Reactome stable ID (e.g. 'R-HSA-6804754')." + ), + ) + parser_reactome.add_argument( + "-r", + "--resource", + type=str, + choices=["pathways", "search", "entity"], + default="pathways", + required=False, + help=( + "Type of query to perform:\n" + " - 'pathways' (default): pathways the identifier participates in.\n" + " - 'search': full-text search of the Reactome knowledgebase.\n" + " - 'entity': details for a Reactome stable ID." + ), + ) + parser_reactome.add_argument( + "-s", + "--source", + type=str, + default="UniProt", + required=False, + help="Identifier resource/database for resource 'pathways' (e.g. 'UniProt', 'Ensembl', 'ChEBI'). Default: UniProt.", + ) + parser_reactome.add_argument( + "-sp", + "--species", + type=str, + default=None, + required=False, + help="Restrict results to a species, as a name (e.g. 'Homo sapiens') or NCBI taxonomy ID (e.g. '9606').", + ) + parser_reactome.add_argument( + "-t", + "--types", + type=str, + default=None, + required=False, + help="Restrict resource 'search' results to one or more entry types (e.g. 'Pathway', 'Reaction', 'Protein').", + ) + parser_reactome.add_argument( + "-o", + "--out", + type=str, + required=False, + help=( + "Path to the file the results will be saved in, e.g. path/to/directory/results.json.\n" + "Default: Standard out." + ), + ) + parser_reactome.add_argument( + "-csv", + "--csv", + default=False, + action="store_true", + required=False, + help="Returns results in csv format instead of json.", + ) + parser_reactome.add_argument( + "-q", + "--quiet", + default=True, + action="store_false", + required=False, + help="Does not print progress information.", + ) + ## g2p parser arguments g2p_desc = "Query the Genomics 2 Proteins (G2P) portal for residue-level protein structure/function annotations." parser_g2p = parent_subparsers.add_parser( @@ -2971,6 +3057,7 @@ def main() -> None: "opentargets": parser_opentargets, "cbio": parser_cbio, "bgee": parser_bgee, + "reactome": parser_reactome, "8cube": parser_8cube, "virus": parser_virus, } @@ -3831,6 +3918,35 @@ def main() -> None: else: print(bgee_results.to_json(orient="records", force_ascii=False, indent=4)) + ## reactome return + if args.command == "reactome": + reactome_results: pd.DataFrame = reactome( + args.query, + resource=args.resource, + source=args.source, + species=args.species, + types=args.types, + verbose=args.quiet, + ) + + if args.out is not None and args.out != "": + # Make saving directory + directory = os.path.dirname(args.out) + if directory != "": + os.makedirs(directory, exist_ok=True) + + # Save results + with open(args.out, "w", encoding="utf-8") as f: + if args.csv: + reactome_results.to_csv(f, index=False) + else: + reactome_results.to_json(f, orient="records", force_ascii=False, indent=4) + else: + if args.csv: + reactome_results.to_csv(sys.stdout, index=False) + else: + print(reactome_results.to_json(orient="records", force_ascii=False, indent=4)) + ## g2p return if args.command == "g2p": residues: list[int] | None = None diff --git a/tests/fixtures/test_reactome.json b/tests/fixtures/test_reactome.json new file mode 100644 index 000000000..0c6f4abe8 --- /dev/null +++ b/tests/fixtures/test_reactome.json @@ -0,0 +1,19 @@ +{ + "test_reactome_invalid_resource": { + "type": "error", + "args": { + "query": "P04637", + "resource": "bogus" + }, + "expected_result": "ValueError", + "expected_msg": "Argument 'resource' must be one of ('pathways', 'search', 'entity'), not 'bogus'." + }, + "test_reactome_empty_query": { + "type": "error", + "args": { + "query": " " + }, + "expected_result": "ValueError", + "expected_msg": "Argument 'query' must be a non-empty string." + } +} diff --git a/tests/test_reactome.py b/tests/test_reactome.py new file mode 100644 index 000000000..5c7df387f --- /dev/null +++ b/tests/test_reactome.py @@ -0,0 +1,77 @@ +import json +import unittest + +import pandas as pd +from gget.gget_reactome import reactome + +from .from_json import from_json + +# Load dictionary containing arguments and expected results for the offline (deterministic) +# argument-validation tests. +with open("./tests/fixtures/test_reactome.json") as json_file: + reactome_dict = json.load(json_file) + +# Reactome content is updated quarterly, so the network tests below assert structural +# invariants (columns, identifier shape, presence of a stable well-known entry) rather than +# exact pathway lists, and skip themselves if the Reactome service is unreachable. + + +class TestReactome(unittest.TestCase, metaclass=from_json(reactome_dict, reactome)): + def _maybe_skip(self, func, **kwargs): + try: + return func(**kwargs) + except RuntimeError as e: # network/transient upstream failure + self.skipTest(f"Reactome service unreachable: {e}") + + def test_reactome_pathways_network(self): + df = self._maybe_skip( + reactome, query="P04637", resource="pathways", species="9606", verbose=False + ) + self.assertIsInstance(df, pd.DataFrame) + self.assertEqual( + list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"] + ) + self.assertGreater(len(df), 0) + # All stable IDs are Reactome identifiers, and the species filter is honored. + self.assertTrue(df["stable_id"].str.startswith("R-").all()) + self.assertTrue((df["species"] == "Homo sapiens").all()) + + def test_reactome_search_network(self): + df = self._maybe_skip( + reactome, query="TP53", resource="search", types="Pathway", verbose=False + ) + self.assertEqual( + list(df.columns), ["stable_id", "name", "type", "species", "reactome_id"] + ) + self.assertGreater(len(df), 0) + # HTML highlight tags from the search endpoint must be stripped from names. + self.assertFalse(df["name"].str.contains("<", regex=False).any()) + + def test_reactome_entity_network(self): + df = self._maybe_skip(reactome, query="R-HSA-6804754", resource="entity", verbose=False) + self.assertEqual(len(df), 1) + self.assertEqual(df.iloc[0]["stable_id"], "R-HSA-6804754") + self.assertEqual(df.iloc[0]["schema_class"], "Pathway") + + def test_reactome_json_output_network(self): + result = self._maybe_skip( + reactome, query="P04637", resource="pathways", species="9606", json=True, verbose=False + ) + self.assertIsInstance(result, list) + self.assertGreater(len(result), 0) + self.assertIn("stable_id", result[0]) + + def test_reactome_pathways_no_results_network(self): + # An unmapped identifier yields an empty DataFrame (Reactome returns HTTP 404), not an error. + df = self._maybe_skip( + reactome, query="NOTAREALID", resource="pathways", verbose=False + ) + self.assertIsInstance(df, pd.DataFrame) + self.assertEqual(len(df), 0) + self.assertEqual( + list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"] + ) + + +if __name__ == "__main__": + unittest.main() From 1488b3be81aca62b7708fa7d0bd93769904c3a10 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:36:05 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- gget/gget_reactome.py | 4 +--- tests/test_reactome.py | 24 ++++++------------------ 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/gget/gget_reactome.py b/gget/gget_reactome.py index 9b8192b46..2eff638d9 100644 --- a/gget/gget_reactome.py +++ b/gget/gget_reactome.py @@ -220,9 +220,7 @@ def reactome( Returns the requested information as a DataFrame (or list of dicts if json=True). """ if resource not in REACTOME_RESOURCES: - raise ValueError( - f"Argument 'resource' must be one of {REACTOME_RESOURCES}, not '{resource}'." - ) + raise ValueError(f"Argument 'resource' must be one of {REACTOME_RESOURCES}, not '{resource}'.") if not isinstance(query, str) or not query.strip(): raise ValueError("Argument 'query' must be a non-empty string.") diff --git a/tests/test_reactome.py b/tests/test_reactome.py index 5c7df387f..f290aff79 100644 --- a/tests/test_reactome.py +++ b/tests/test_reactome.py @@ -24,25 +24,17 @@ def _maybe_skip(self, func, **kwargs): self.skipTest(f"Reactome service unreachable: {e}") def test_reactome_pathways_network(self): - df = self._maybe_skip( - reactome, query="P04637", resource="pathways", species="9606", verbose=False - ) + df = self._maybe_skip(reactome, query="P04637", resource="pathways", species="9606", verbose=False) self.assertIsInstance(df, pd.DataFrame) - self.assertEqual( - list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"] - ) + self.assertEqual(list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"]) self.assertGreater(len(df), 0) # All stable IDs are Reactome identifiers, and the species filter is honored. self.assertTrue(df["stable_id"].str.startswith("R-").all()) self.assertTrue((df["species"] == "Homo sapiens").all()) def test_reactome_search_network(self): - df = self._maybe_skip( - reactome, query="TP53", resource="search", types="Pathway", verbose=False - ) - self.assertEqual( - list(df.columns), ["stable_id", "name", "type", "species", "reactome_id"] - ) + df = self._maybe_skip(reactome, query="TP53", resource="search", types="Pathway", verbose=False) + self.assertEqual(list(df.columns), ["stable_id", "name", "type", "species", "reactome_id"]) self.assertGreater(len(df), 0) # HTML highlight tags from the search endpoint must be stripped from names. self.assertFalse(df["name"].str.contains("<", regex=False).any()) @@ -63,14 +55,10 @@ def test_reactome_json_output_network(self): def test_reactome_pathways_no_results_network(self): # An unmapped identifier yields an empty DataFrame (Reactome returns HTTP 404), not an error. - df = self._maybe_skip( - reactome, query="NOTAREALID", resource="pathways", verbose=False - ) + df = self._maybe_skip(reactome, query="NOTAREALID", resource="pathways", verbose=False) self.assertIsInstance(df, pd.DataFrame) self.assertEqual(len(df), 0) - self.assertEqual( - list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"] - ) + self.assertEqual(list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"]) if __name__ == "__main__": From 3ef868a41b6d44c1c4b0b2c5c3b0c14f63856c72 Mon Sep 17 00:00:00 2001 From: Elarwei Date: Thu, 25 Jun 2026 00:20:30 +0800 Subject: [PATCH 3/5] test(reactome): cover remaining lines for codecov (#114) Add a network-free TestReactomeOffline class that mocks http_json to cover _strip_html passthrough, the pathways/search/entity parsing, verbose logging, 404 and non-404 error branches, the entity name fallback/summation, and resource/query validation. gget_reactome.py now 98% (only the import-time PackageNotFoundError version fallback, lines 16-17, remains, which is un-coverable in an installed environment). Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/test_reactome.py | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/test_reactome.py b/tests/test_reactome.py index f290aff79..148d27966 100644 --- a/tests/test_reactome.py +++ b/tests/test_reactome.py @@ -1,6 +1,8 @@ import json import unittest +from unittest.mock import patch +import gget.gget_reactome as gget_reactome import pandas as pd from gget.gget_reactome import reactome @@ -61,5 +63,113 @@ def test_reactome_pathways_no_results_network(self): self.assertEqual(list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"]) +class TestReactomeOffline(unittest.TestCase): + """Network-free tests of the Reactome parsing/branch logic (issue #114). + + All HTTP access goes through gget_reactome.http_json, which is mocked here. + """ + + def test_strip_html_passthrough(self): + # Non-string values pass through unchanged. + self.assertIsNone(gget_reactome._strip_html(None)) + self.assertEqual(gget_reactome._strip_html(123), 123) + self.assertEqual(gget_reactome._strip_html("TP53"), "TP53") + + @patch.object(gget_reactome, "http_json") + def test_pathways_parsing_verbose(self, mock_http): + mock_http.return_value = [ + { + "stId": "R-HSA-1", + "displayName": "Pathway A", + "speciesName": "Homo sapiens", + "schemaClass": "Pathway", + "isInDisease": False, + } + ] + df = reactome("P04637", resource="pathways", species="Homo sapiens", verbose=True) + self.assertEqual(df.iloc[0]["stable_id"], "R-HSA-1") + self.assertEqual(df.iloc[0]["species"], "Homo sapiens") + + @patch.object(gget_reactome, "http_json") + def test_pathways_404_returns_empty(self, mock_http): + mock_http.side_effect = RuntimeError("Reactome returned HTTP 404") + df = reactome("NOTREAL", resource="pathways", verbose=False) + self.assertEqual(len(df), 0) + + @patch.object(gget_reactome, "http_json") + def test_pathways_other_error_raises(self, mock_http): + mock_http.side_effect = RuntimeError("Reactome returned HTTP 500") + with self.assertRaises(RuntimeError): + reactome("P04637", resource="pathways", verbose=False) + + @patch.object(gget_reactome, "http_json") + def test_search_parsing_species_types_verbose(self, mock_http): + mock_http.return_value = { + "results": [ + { + "entries": [ + { + "stId": "R-HSA-9", + "name": "TP53", + "exactType": "Protein", + "species": ["Homo sapiens"], + "id": 12345, + } + ] + } + ] + } + df = reactome("TP53", resource="search", species="9606", types="Protein", verbose=True) + self.assertEqual(df.iloc[0]["stable_id"], "R-HSA-9") + self.assertEqual(df.iloc[0]["name"], "TP53") # HTML stripped + self.assertEqual(df.iloc[0]["species"], "Homo sapiens") # list flattened + + @patch.object(gget_reactome, "http_json") + def test_search_404_returns_empty(self, mock_http): + mock_http.side_effect = RuntimeError("Reactome returned HTTP 404") + df = reactome("zzzz", resource="search", verbose=False) + self.assertEqual(len(df), 0) + + @patch.object(gget_reactome, "http_json") + def test_search_other_error_raises(self, mock_http): + mock_http.side_effect = RuntimeError("Reactome returned HTTP 503") + with self.assertRaises(RuntimeError): + reactome("TP53", resource="search", verbose=False) + + @patch.object(gget_reactome, "http_json") + def test_entity_name_fallback_and_summation(self, mock_http): + # displayName missing -> fall back to first of name list; summation HTML stripped. + mock_http.return_value = { + "stId": "R-HSA-6804754", + "displayName": None, + "name": ["Regulation of TP53 Activity"], + "schemaClass": "Pathway", + "speciesName": "Homo sapiens", + "isInDisease": False, + "summation": [{"text": "

Some summary.

"}], + } + result = reactome("R-HSA-6804754", resource="entity", json=True, verbose=True) + self.assertEqual(result[0]["name"], "Regulation of TP53 Activity") + self.assertEqual(result[0]["summation"], "Some summary.") + + @patch.object(gget_reactome, "http_json") + def test_entity_404_raises_valueerror(self, mock_http): + mock_http.side_effect = RuntimeError("Reactome returned HTTP 404") + with self.assertRaises(ValueError): + reactome("R-HSA-0", resource="entity", verbose=False) + + @patch.object(gget_reactome, "http_json") + def test_entity_other_error_raises(self, mock_http): + mock_http.side_effect = RuntimeError("Reactome returned HTTP 500") + with self.assertRaises(RuntimeError): + reactome("R-HSA-6804754", resource="entity", verbose=False) + + def test_invalid_resource_and_empty_query(self): + with self.assertRaises(ValueError): + reactome("TP53", resource="banana", verbose=False) + with self.assertRaises(ValueError): + reactome(" ", resource="pathways", verbose=False) + + if __name__ == "__main__": unittest.main() From 22f47ec49798746bd04642cbeb32f81448807375 Mon Sep 17 00:00:00 2001 From: Elarwei Date: Mon, 6 Jul 2026 21:46:18 +0800 Subject: [PATCH 4/5] feat(reactome): add interactors/orthology/event-hierarchy + review fixes (#114) New resource query types for gget reactome, plus the review follow-ups: - resource="interactors": molecular interactors of an identifier (IntAct static), columns [interactor_acc, interactor_name, score, evidences]. - resource="orthology": project a Reactome stable ID to its ortholog in another species (requires species; resolves the species name/taxId to Reactome's dbId). - resource="event-hierarchy": the full pathway/reaction hierarchy for a species, flattened to one row per event with parent_id and nesting level. Review fixes: - Fix the CLI type annotation that failed pre-commit.ci (main.py: cast the reactome() result, which is a DataFrame in the CLI path). - Structured 404 detection: http_json now raises HTTPStatusError (a RuntimeError subclass carrying .status_code), and reactome checks the code instead of string-matching the message. Backward-compatible for existing http_json callers. - Record the Reactome release in df.attrs["reactome_release"] for reproducibility. - Add a network test that passes species as a name (not just a taxon ID). Tests: offline mocks for the three new resources (+ species resolution and error branches) and network invariant tests that skip when Reactome is unreachable. Docs: reactome.md resource list, --resource/--species, examples; changelog. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/src/en/reactome.md | 55 ++++++++- docs/src/en/updates.md | 2 +- gget/gget_reactome.py | 190 +++++++++++++++++++++++++++--- gget/main.py | 27 +++-- gget/utils.py | 23 +++- tests/fixtures/test_reactome.json | 2 +- tests/test_reactome.py | 117 +++++++++++++++++- 7 files changed, 378 insertions(+), 38 deletions(-) diff --git a/docs/src/en/reactome.md b/docs/src/en/reactome.md index 2ae0c50e2..4e8bd59db 100644 --- a/docs/src/en/reactome.md +++ b/docs/src/en/reactome.md @@ -5,27 +5,33 @@ Query the [Reactome](https://reactome.org/) pathway knowledgebase using its [ContentService REST API](https://reactome.org/dev/content-service). Return format: JSON/CSV (command-line) or data frame (Python). -`gget reactome` supports three kinds of queries, selected with the `resource` argument: +`gget reactome` supports several kinds of queries, selected with the `resource` argument: - `pathways` (default): return the Reactome pathways a given identifier (e.g. a UniProt accession) participates in. - `search`: full-text search of the Reactome knowledgebase (pathways, reactions, physical entities, ...). - `entity`: return details for a Reactome stable ID (e.g. `R-HSA-6804754`). +- `interactors`: return the molecular interactors of an identifier (IntAct static interactors). +- `orthology`: project a Reactome stable ID to its ortholog in another species (requires `--species`). +- `event-hierarchy`: return the full pathway/reaction hierarchy for a species. **Positional argument** `query` -Identifier or search term to query. Its meaning depends on `resource`: +Identifier, search term or species to query. Its meaning depends on `resource`: - `resource="pathways"`: an identifier, e.g. UniProt accession `P04637`. - `resource="search"`: a free-text search term, e.g. `TP53`. - `resource="entity"`: a Reactome stable ID, e.g. `R-HSA-6804754`. +- `resource="interactors"`: a molecule accession, e.g. UniProt `P04637`. +- `resource="orthology"`: a Reactome stable ID to project, e.g. `R-HSA-6804754`. +- `resource="event-hierarchy"`: a species name or NCBI taxonomy ID, e.g. `Homo sapiens`. **Optional arguments** `-r` `--resource` -Type of query to perform. Options: `pathways` (default), `search`, `entity`. +Type of query to perform. Options: `pathways` (default), `search`, `entity`, `interactors`, `orthology`, `event-hierarchy`. `-s` `--source` Identifier resource/database for `resource="pathways"`, e.g. `UniProt` (default), `Ensembl`, `ChEBI`, `NCBI`. `-sp` `--species` -Restrict results to a species, as a name (e.g. `Homo sapiens`) or NCBI taxonomy ID (e.g. `9606`). Applies to `resource="pathways"` and `resource="search"`. Default: None (no species filter). +Species, as a name (e.g. `Homo sapiens`) or NCBI taxonomy ID (e.g. `9606`). Filters `resource="pathways"` and `resource="search"`; is the **required target** for `resource="orthology"`. Default: None. `-t` `--types` Restrict `resource="search"` results to one or more entry types, e.g. `Pathway`, `Reaction`, `Protein`. Default: None (all types). @@ -102,6 +108,47 @@ gget.reactome("R-HSA-6804754", resource="entity") |---------------|-------------------------------|--------------|--------------|------------|------------------| | R-HSA-6804754 | Regulation of TP53 Expression | Pathway | Homo sapiens | False | Transcription... | +

+ +**Get the molecular interactors of a protein** + +```bash +gget reactome P04637 -r interactors +``` +```python +gget.reactome("P04637", resource="interactors") +``` + +→ Returns the IntAct interactors of P04637 (columns: `interactor_acc`, `interactor_name`, `score`, `evidences`), e.g. MDM2, with an interaction confidence score. + +

+ +**Project a pathway to another species (orthology)** + +```bash +gget reactome R-HSA-6804754 -r orthology -sp "Mus musculus" +``` +```python +gget.reactome("R-HSA-6804754", resource="orthology", species="Mus musculus") +``` + +→ Returns the mouse ortholog of the human pathway (`R-MMU-6804754`, "Regulation of TP53 Expression", Mus musculus). + +

+ +**Get the full event (pathway/reaction) hierarchy for a species** + +```bash +gget reactome "Homo sapiens" -r event-hierarchy +``` +```python +gget.reactome("Homo sapiens", resource="event-hierarchy") +``` + +→ Returns the entire human event hierarchy flattened to one row per event (columns: `stable_id`, `name`, `type`, `species`, `parent_id`, `level`). This is a large table — use `parent_id`/`level` to navigate the tree. + +> The returned DataFrame's `.attrs["reactome_release"]` records the Reactome release version (e.g. `97`) for reproducibility. + # References If you use `gget reactome` in a publication, please cite the following articles: diff --git a/docs/src/en/updates.md b/docs/src/en/updates.md index 8aa0c5850..b1d5dda5b 100644 --- a/docs/src/en/updates.md +++ b/docs/src/en/updates.md @@ -5,7 +5,7 @@ #### *gget* officially became part of [*scverse*](https://scverse.org/) on June 9, 2026. 🥳🥳🥳 **Version ≥ 0.30.9** (XXX XX, 2026): -- [`gget reactome`](reactome.md): **New module** to query the [Reactome](https://reactome.org/) pathway knowledgebase via its ContentService REST API. Supports three query types via the `resource` argument: `pathways` (the Reactome pathways an identifier such as a UniProt accession participates in), `search` (full-text search of the knowledgebase), and `entity` (details for a Reactome stable ID). Resolves [issue 114](https://github.com/scverse/gget/issues/114). +- [`gget reactome`](reactome.md): **New module** to query the [Reactome](https://reactome.org/) pathway knowledgebase via its ContentService REST API. Query types are selected with the `resource` argument: `pathways` (the pathways an identifier such as a UniProt accession participates in), `search` (full-text search), `entity` (details for a Reactome stable ID), `interactors` (molecular interactors of an identifier), `orthology` (project a stable ID to its ortholog in another species), and `event-hierarchy` (the full pathway/reaction hierarchy for a species). Returned data frames carry the Reactome release version in `.attrs["reactome_release"]` for reproducibility. Resolves [issue 114](https://github.com/scverse/gget/issues/114). **Version ≥ 0.30.8** (Jun 28, 2026): - [`gget g2p`](g2p.md): Either `gene` or `--uniprot_id` is now sufficient — whichever is missing is resolved via UniProt and cached. Gene→UniProt picks the canonical reviewed human Swiss-Prot entry; the resolution and its limitations are logged. The canonical pair is **always** prepended to the result as `gene_name` / `uniprot_id` columns (and stored on `df.attrs`), so the output schema is invariant regardless of input mode. Existing call sites continue to work. diff --git a/gget/gget_reactome.py b/gget/gget_reactome.py index 2eff638d9..57f56bfdc 100644 --- a/gget/gget_reactome.py +++ b/gget/gget_reactome.py @@ -7,7 +7,7 @@ import pandas as pd -from .utils import http_json, set_up_logger +from .utils import HTTPStatusError, http_json, set_up_logger logger = set_up_logger() @@ -25,7 +25,7 @@ } # Supported values for the 'resource' argument -REACTOME_RESOURCES = ("pathways", "search", "entity") +REACTOME_RESOURCES = ("pathways", "search", "entity", "interactors", "orthology", "event-hierarchy") # Regex to strip the ... tags Reactome wraps around # matched search terms. @@ -40,8 +40,18 @@ def _strip_html(text: Any) -> Any: def _is_http_status(error: Exception, status: int) -> bool: - """True if the RuntimeError raised by http_json corresponds to the given HTTP status.""" - return f"HTTP {status}" in str(error) + """True if the error raised by http_json corresponds to the given HTTP status.""" + return isinstance(error, HTTPStatusError) and error.status_code == status + + +def _reactome_release() -> str | None: + """Return the current Reactome release version (e.g. '97'), or None if unavailable.""" + # This endpoint returns a plain-text integer; requesting application/json yields HTTP 406. + headers = {**_REACTOME_HEADERS, "Accept": "text/plain"} + try: + return str(http_json("GET", f"{REACTOME_CONTENT_API}/data/database/version", headers=headers)).strip() + except (RuntimeError, ValueError): + return None def _reactome_pathways( @@ -185,6 +195,138 @@ def _reactome_entity(query: str, verbose: bool = True) -> pd.DataFrame: return pd.DataFrame([row], columns=["stable_id", "name", "schema_class", "species", "in_disease", "summation"]) +def _reactome_species_dbid(species: str) -> int: + """Resolve a species name or NCBI taxonomy ID to its Reactome species dbId.""" + payload = http_json("GET", f"{REACTOME_CONTENT_API}/data/species/main", headers=_REACTOME_HEADERS) + wanted = str(species).strip().lower() + for entry in payload: + if wanted in (str(entry.get("displayName", "")).lower(), str(entry.get("taxId", "")).lower()): + return int(entry["dbId"]) + raise ValueError( + f"Species '{species}' was not found among Reactome's species. Provide a Reactome species " + "name (e.g. 'Mus musculus') or NCBI taxonomy ID (e.g. '10090')." + ) + + +def _reactome_interactors(query: str, verbose: bool = True) -> pd.DataFrame: + """Return the molecular interactors of an identifier (IntAct static interactors). + + Uses the ContentService '/interactors/static/molecule/{acc}/details' endpoint. + """ + if verbose: + logger.info(f"Querying Reactome interactors for '{query}'.") + + columns = ["interactor_acc", "interactor_name", "score", "evidences"] + try: + payload = http_json( + "GET", + f"{REACTOME_CONTENT_API}/interactors/static/molecule/{query}/details", + context="Reactome ContentService (interactors)", + headers=_REACTOME_HEADERS, + ) + except RuntimeError as e: + if _is_http_status(e, 404): + logger.warning(f"No Reactome interactors found for '{query}'.") + return pd.DataFrame(columns=columns) + raise + + entities = payload.get("entities", []) + interactors = entities[0].get("interactors", []) if entities else [] + rows = [ + { + "interactor_acc": it.get("acc"), + "interactor_name": it.get("alias"), + "score": it.get("score"), + "evidences": it.get("evidences"), + } + for it in interactors + ] + return pd.DataFrame(rows, columns=columns) + + +def _reactome_orthology(query: str, species: str | None, verbose: bool = True) -> pd.DataFrame: + """Project a Reactome event/entity to its ortholog in another species. + + Uses the ContentService '/data/orthology/{id}/species/{speciesDbId}' endpoint. + """ + if species is None: + raise ValueError("resource='orthology' requires a target 'species' (e.g. species='Mus musculus' or '10090').") + + species_dbid = _reactome_species_dbid(species) + if verbose: + logger.info(f"Projecting Reactome entry '{query}' to species '{species}'.") + + try: + payload = http_json( + "GET", + f"{REACTOME_CONTENT_API}/data/orthology/{query}/species/{species_dbid}", + context="Reactome ContentService (orthology)", + headers=_REACTOME_HEADERS, + ) + except RuntimeError as e: + if _is_http_status(e, 404): + raise ValueError( + f"No Reactome ortholog found for '{query}' in species '{species}'. Provide a valid " + "Reactome stable ID (e.g. 'R-HSA-6804754') and a species Reactome covers." + ) from e + raise + + row = { + "stable_id": payload.get("stId"), + "name": payload.get("displayName"), + "species": payload.get("speciesName"), + "schema_class": payload.get("schemaClass"), + } + return pd.DataFrame([row], columns=["stable_id", "name", "species", "schema_class"]) + + +def _reactome_event_hierarchy(query: str, verbose: bool = True) -> pd.DataFrame: + """Return the full event (pathway/reaction) hierarchy for a species as a flat adjacency table. + + Uses the ContentService '/data/eventsHierarchy/{species}' endpoint; the nested tree is flattened + to one row per event with its parent_id and nesting level. + """ + if verbose: + logger.info(f"Fetching the Reactome event hierarchy for species '{query}'.") + + columns = ["stable_id", "name", "type", "species", "parent_id", "level"] + try: + payload = http_json( + "GET", + f"{REACTOME_CONTENT_API}/data/eventsHierarchy/{query}", + context="Reactome ContentService (event-hierarchy)", + headers=_REACTOME_HEADERS, + ) + except RuntimeError as e: + if _is_http_status(e, 404): + raise ValueError( + f"No Reactome event hierarchy found for species '{query}'. Provide a Reactome species " + "name (e.g. 'Homo sapiens') or NCBI taxonomy ID (e.g. '9606')." + ) from e + raise + + rows: list[dict[str, Any]] = [] + + def _walk(node: dict[str, Any], parent_id: str | None, level: int) -> None: + rows.append( + { + "stable_id": node.get("stId"), + "name": node.get("name"), + "type": node.get("type"), + "species": node.get("species"), + "parent_id": parent_id, + "level": level, + } + ) + for child in node.get("children", []) or []: + _walk(child, node.get("stId"), level + 1) + + for top in payload: + _walk(top, None, 0) + + return pd.DataFrame(rows, columns=columns) + + def reactome( query: str, resource: str = "pathways", @@ -197,27 +339,34 @@ def reactome( """Query the Reactome pathway knowledgebase (https://reactome.org/). Args: - - query Identifier or search term to query (str). Its meaning depends on 'resource': - - resource="pathways": an identifier (e.g. UniProt accession 'P04637') whose - Reactome pathways should be returned. - - resource="search": a free-text search term (e.g. 'TP53'). - - resource="entity": a Reactome stable ID (e.g. 'R-HSA-6804754'). + - query Identifier, search term or species to query (str). Its meaning depends on 'resource': + - resource="pathways": an identifier (e.g. UniProt accession 'P04637'). + - resource="search": a free-text search term (e.g. 'TP53'). + - resource="entity": a Reactome stable ID (e.g. 'R-HSA-6804754'). + - resource="interactors": a molecule accession (e.g. UniProt 'P04637'). + - resource="orthology": a Reactome stable ID to project to another species. + - resource="event-hierarchy": a species name or NCBI taxonomy ID. - resource Type of query to perform (str). One of: - "pathways" (default): pathways the identifier participates in. - "search": full-text search of the Reactome knowledgebase. - "entity": details for a Reactome stable ID. + - "interactors": molecular interactors of an identifier (IntAct static). + - "orthology": project a stable ID to its ortholog in another species + (requires 'species'). + - "event-hierarchy": the full pathway/reaction hierarchy for a species. - source Identifier resource/database for resource="pathways" (str), e.g. 'UniProt', 'Ensembl', 'ChEBI', 'NCBI'. Default: "UniProt". - - species Restrict results to a species (str), as a name (e.g. 'Homo sapiens') or NCBI - taxonomy ID (e.g. '9606'). Applies to resource="pathways" and resource="search". - Default: None (no species filter). + - species Species as a name (e.g. 'Homo sapiens') or NCBI taxonomy ID (e.g. '9606'). + Filters resource="pathways"/"search"; is the required target for resource="orthology". + Default: None. - types Restrict resource="search" results to one or more entry types (str), e.g. 'Pathway', 'Reaction', 'Protein'. Default: None (all types). - json If True, return the result as a JSON-serializable list of dicts instead of a DataFrame (default: False). - verbose True/False whether to print progress information. Default: True. - Returns the requested information as a DataFrame (or list of dicts if json=True). + Returns the requested information as a DataFrame (or list of dicts if json=True). The DataFrame's + `.attrs["reactome_release"]` carries the Reactome release version, for reproducibility. """ if resource not in REACTOME_RESOURCES: raise ValueError(f"Argument 'resource' must be one of {REACTOME_RESOURCES}, not '{resource}'.") @@ -229,8 +378,21 @@ def reactome( df = _reactome_pathways(query, source=source, species=species, verbose=verbose) elif resource == "search": df = _reactome_search(query, species=species, types=types, verbose=verbose) - else: # resource == "entity" + elif resource == "entity": df = _reactome_entity(query, verbose=verbose) + elif resource == "interactors": + df = _reactome_interactors(query, verbose=verbose) + elif resource == "orthology": + df = _reactome_orthology(query, species=species, verbose=verbose) + else: # resource == "event-hierarchy" + df = _reactome_event_hierarchy(query, verbose=verbose) + + # Record the Reactome release for reproducibility (best-effort; non-fatal). + release = _reactome_release() + if release is not None: + df.attrs["reactome_release"] = release + if verbose: + logger.info(f"Reactome release: {release}.") if json: return json_.loads(df.to_json(orient="records", force_ascii=False)) diff --git a/gget/main.py b/gget/main.py index eb8afaa0e..10aa2a1e9 100644 --- a/gget/main.py +++ b/gget/main.py @@ -3,7 +3,7 @@ import argparse import sys from datetime import datetime -from typing import Any +from typing import Any, cast import pandas as pd @@ -2367,14 +2367,17 @@ def main() -> None: "-r", "--resource", type=str, - choices=["pathways", "search", "entity"], + choices=["pathways", "search", "entity", "interactors", "orthology", "event-hierarchy"], default="pathways", required=False, help=( "Type of query to perform:\n" " - 'pathways' (default): pathways the identifier participates in.\n" " - 'search': full-text search of the Reactome knowledgebase.\n" - " - 'entity': details for a Reactome stable ID." + " - 'entity': details for a Reactome stable ID.\n" + " - 'interactors': molecular interactors of an identifier.\n" + " - 'orthology': project a stable ID to another species (needs --species).\n" + " - 'event-hierarchy': the full pathway/reaction hierarchy for a species." ), ) parser_reactome.add_argument( @@ -3920,13 +3923,17 @@ def main() -> None: ## reactome return if args.command == "reactome": - reactome_results: pd.DataFrame = reactome( - args.query, - resource=args.resource, - source=args.source, - species=args.species, - types=args.types, - verbose=args.quiet, + # The CLI never passes json=True, so reactome() always returns a DataFrame here. + reactome_results = cast( + "pd.DataFrame", + reactome( + args.query, + resource=args.resource, + source=args.source, + species=args.species, + types=args.types, + verbose=args.quiet, + ), ) if args.out is not None and args.out != "": diff --git a/gget/utils.py b/gget/utils.py index 040730e39..c6171ccce 100644 --- a/gget/utils.py +++ b/gget/utils.py @@ -98,6 +98,19 @@ def parallel_map(fn: Callable[[Any], Any], items: Iterable[Any], *, max_workers: return list(executor.map(fn, items)) +class HTTPStatusError(RuntimeError): + """RuntimeError raised by http_json for a non-2xx HTTP response. + + Subclasses RuntimeError (so existing `except RuntimeError` handlers keep working) but + carries the numeric `status_code`, letting callers branch on it without parsing the + error message. + """ + + def __init__(self, message: str, status_code: int | None = None) -> None: + super().__init__(message) + self.status_code = status_code + + def http_json( method: str, url: str, @@ -145,7 +158,10 @@ def http_json( # to the caller without retry. if response.status_code < 500: body = response.text[:200] if response.text else "" - raise RuntimeError(f"{label} returned HTTP {response.status_code}. Body: {body}") + raise HTTPStatusError( + f"{label} returned HTTP {response.status_code}. Body: {body}", + status_code=response.status_code, + ) last_exc = None last_status = response.status_code last_body = response.text[:200] if response.text else "" @@ -163,7 +179,10 @@ def http_json( if last_exc is not None: raise RuntimeError(f"{label} request failed after {attempts} attempts: {last_exc}") from last_exc - raise RuntimeError(f"{label} returned HTTP {last_status} after {attempts} attempts. Body: {last_body}") + raise HTTPStatusError( + f"{label} returned HTTP {last_status} after {attempts} attempts. Body: {last_body}", + status_code=last_status, + ) def dig(obj: Any, *path: str, context: str = "") -> Any: diff --git a/tests/fixtures/test_reactome.json b/tests/fixtures/test_reactome.json index 0c6f4abe8..ffaf218da 100644 --- a/tests/fixtures/test_reactome.json +++ b/tests/fixtures/test_reactome.json @@ -6,7 +6,7 @@ "resource": "bogus" }, "expected_result": "ValueError", - "expected_msg": "Argument 'resource' must be one of ('pathways', 'search', 'entity'), not 'bogus'." + "expected_msg": "Argument 'resource' must be one of ('pathways', 'search', 'entity', 'interactors', 'orthology', 'event-hierarchy'), not 'bogus'." }, "test_reactome_empty_query": { "type": "error", diff --git a/tests/test_reactome.py b/tests/test_reactome.py index 148d27966..25b5ca693 100644 --- a/tests/test_reactome.py +++ b/tests/test_reactome.py @@ -5,9 +5,16 @@ import gget.gget_reactome as gget_reactome import pandas as pd from gget.gget_reactome import reactome +from gget.utils import HTTPStatusError from .from_json import from_json + +def _http_error(status: int) -> HTTPStatusError: + """Build the structured HTTP error that http_json raises for a given status.""" + return HTTPStatusError(f"Reactome returned HTTP {status}.", status_code=status) + + # Load dictionary containing arguments and expected results for the offline (deterministic) # argument-validation tests. with open("./tests/fixtures/test_reactome.json") as json_file: @@ -62,6 +69,32 @@ def test_reactome_pathways_no_results_network(self): self.assertEqual(len(df), 0) self.assertEqual(list(df.columns), ["stable_id", "name", "species", "schema_class", "in_disease"]) + def test_reactome_pathways_species_name_network(self): + # Species passed as a NAME (not a taxon ID) is honored just like the numeric ID. + df = self._maybe_skip(reactome, query="P04637", resource="pathways", species="Homo sapiens", verbose=False) + self.assertGreater(len(df), 0) + self.assertTrue((df["species"] == "Homo sapiens").all()) + + def test_reactome_interactors_network(self): + df = self._maybe_skip(reactome, query="P04637", resource="interactors", verbose=False) + self.assertEqual(list(df.columns), ["interactor_acc", "interactor_name", "score", "evidences"]) + self.assertGreater(len(df), 0) + + def test_reactome_orthology_network(self): + df = self._maybe_skip( + reactome, query="R-HSA-6804754", resource="orthology", species="Mus musculus", verbose=False + ) + self.assertEqual(len(df), 1) + self.assertEqual(df.iloc[0]["species"], "Mus musculus") + self.assertTrue(df.iloc[0]["stable_id"].startswith("R-MMU-")) + + def test_reactome_event_hierarchy_network(self): + df = self._maybe_skip(reactome, query="9606", resource="event-hierarchy", verbose=False) + self.assertEqual(list(df.columns), ["stable_id", "name", "type", "species", "parent_id", "level"]) + self.assertGreater(len(df), 0) + # Top-level events (level 0) have no parent. + self.assertTrue(df[df["level"] == 0]["parent_id"].isna().all()) + class TestReactomeOffline(unittest.TestCase): """Network-free tests of the Reactome parsing/branch logic (issue #114). @@ -92,13 +125,13 @@ def test_pathways_parsing_verbose(self, mock_http): @patch.object(gget_reactome, "http_json") def test_pathways_404_returns_empty(self, mock_http): - mock_http.side_effect = RuntimeError("Reactome returned HTTP 404") + mock_http.side_effect = _http_error(404) df = reactome("NOTREAL", resource="pathways", verbose=False) self.assertEqual(len(df), 0) @patch.object(gget_reactome, "http_json") def test_pathways_other_error_raises(self, mock_http): - mock_http.side_effect = RuntimeError("Reactome returned HTTP 500") + mock_http.side_effect = _http_error(500) with self.assertRaises(RuntimeError): reactome("P04637", resource="pathways", verbose=False) @@ -126,13 +159,13 @@ def test_search_parsing_species_types_verbose(self, mock_http): @patch.object(gget_reactome, "http_json") def test_search_404_returns_empty(self, mock_http): - mock_http.side_effect = RuntimeError("Reactome returned HTTP 404") + mock_http.side_effect = _http_error(404) df = reactome("zzzz", resource="search", verbose=False) self.assertEqual(len(df), 0) @patch.object(gget_reactome, "http_json") def test_search_other_error_raises(self, mock_http): - mock_http.side_effect = RuntimeError("Reactome returned HTTP 503") + mock_http.side_effect = _http_error(503) with self.assertRaises(RuntimeError): reactome("TP53", resource="search", verbose=False) @@ -154,13 +187,13 @@ def test_entity_name_fallback_and_summation(self, mock_http): @patch.object(gget_reactome, "http_json") def test_entity_404_raises_valueerror(self, mock_http): - mock_http.side_effect = RuntimeError("Reactome returned HTTP 404") + mock_http.side_effect = _http_error(404) with self.assertRaises(ValueError): reactome("R-HSA-0", resource="entity", verbose=False) @patch.object(gget_reactome, "http_json") def test_entity_other_error_raises(self, mock_http): - mock_http.side_effect = RuntimeError("Reactome returned HTTP 500") + mock_http.side_effect = _http_error(500) with self.assertRaises(RuntimeError): reactome("R-HSA-6804754", resource="entity", verbose=False) @@ -170,6 +203,78 @@ def test_invalid_resource_and_empty_query(self): with self.assertRaises(ValueError): reactome(" ", resource="pathways", verbose=False) + # --- interactors / orthology / event-hierarchy (issue #114 follow-up) --- + # _reactome_release is patched off so it doesn't consume an extra mocked http_json call. + + @patch.object(gget_reactome, "_reactome_release", return_value=None) + @patch.object(gget_reactome, "http_json") + def test_interactors_parsing_verbose(self, mock_http, _rel): + mock_http.return_value = { + "entities": [ + {"acc": "P04637", "interactors": [{"acc": "Q00987", "alias": "MDM2", "score": 0.99, "evidences": 122}]} + ] + } + df = reactome("P04637", resource="interactors", verbose=True) + self.assertEqual(list(df.columns), ["interactor_acc", "interactor_name", "score", "evidences"]) + self.assertEqual(df.iloc[0]["interactor_name"], "MDM2") + + @patch.object(gget_reactome, "_reactome_release", return_value=None) + @patch.object(gget_reactome, "http_json") + def test_interactors_404_returns_empty(self, mock_http, _rel): + mock_http.side_effect = _http_error(404) + df = reactome("NOTREAL", resource="interactors", verbose=False) + self.assertEqual(len(df), 0) + + def test_orthology_requires_species(self): + with self.assertRaises(ValueError): + reactome("R-HSA-6804754", resource="orthology", verbose=False) + + @patch.object(gget_reactome, "_reactome_release", return_value=None) + @patch.object(gget_reactome, "http_json") + def test_orthology_parsing(self, mock_http, _rel): + # First call resolves the species dbId; second returns the ortholog. + mock_http.side_effect = [ + [{"dbId": 48892, "displayName": "Mus musculus", "taxId": 10090}], + { + "stId": "R-MMU-6804754", + "displayName": "Regulation of TP53 Expression", + "speciesName": "Mus musculus", + "schemaClass": "Pathway", + }, + ] + df = reactome("R-HSA-6804754", resource="orthology", species="Mus musculus", verbose=True) + self.assertEqual(df.iloc[0]["stable_id"], "R-MMU-6804754") + self.assertEqual(df.iloc[0]["species"], "Mus musculus") + + @patch.object(gget_reactome, "_reactome_release", return_value=None) + @patch.object(gget_reactome, "http_json") + def test_orthology_unknown_species_raises(self, mock_http, _rel): + mock_http.return_value = [{"dbId": 48887, "displayName": "Homo sapiens", "taxId": 9606}] + with self.assertRaises(ValueError): + reactome("R-HSA-6804754", resource="orthology", species="Nonexistent sp", verbose=False) + + @patch.object(gget_reactome, "_reactome_release", return_value=None) + @patch.object(gget_reactome, "http_json") + def test_event_hierarchy_flattening(self, mock_http, _rel): + mock_http.return_value = [ + { + "stId": "R-HSA-1", + "name": "Top", + "type": "TopLevelPathway", + "species": "Homo sapiens", + "children": [ + {"stId": "R-HSA-2", "name": "Sub", "type": "Pathway", "species": "Homo sapiens", "children": []} + ], + } + ] + df = reactome("9606", resource="event-hierarchy", verbose=True) + self.assertEqual(list(df.columns), ["stable_id", "name", "type", "species", "parent_id", "level"]) + self.assertEqual(len(df), 2) + self.assertTrue(pd.isna(df.iloc[0]["parent_id"])) + self.assertEqual(df.iloc[0]["level"], 0) + self.assertEqual(df.iloc[1]["parent_id"], "R-HSA-1") + self.assertEqual(df.iloc[1]["level"], 1) + if __name__ == "__main__": unittest.main() From 5e3b536a8403ef736d07def33115fbd160ceef27 Mon Sep 17 00:00:00 2001 From: Elarwei Date: Mon, 6 Jul 2026 21:53:25 +0800 Subject: [PATCH 5/5] docs(reactome): source citation first, drop issue link, add Spanish page - References: list the Reactome (data source) citation before gget's own. - Remove the "Resolves issue 114" text from the changelog entry (doc pages don't need the issue link). - Add docs/src/es/reactome.md (Spanish translation) and its SUMMARY entry. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/src/SUMMARY.md | 1 + docs/src/en/reactome.md | 4 +- docs/src/en/updates.md | 2 +- docs/src/es/reactome.md | 158 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 docs/src/es/reactome.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b34faa745..a57874262 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -72,6 +72,7 @@ * [gget mutate](es/mutate.md) * [gget opentargets](es/opentargets.md) * [gget pdb](es/pdb.md) +* [gget reactome](es/reactome.md) * [gget ref](es/ref.md) * [gget search](es/search.md) * [gget setup](es/setup.md) diff --git a/docs/src/en/reactome.md b/docs/src/en/reactome.md index 4e8bd59db..5d24685c3 100644 --- a/docs/src/en/reactome.md +++ b/docs/src/en/reactome.md @@ -153,6 +153,6 @@ gget.reactome("Homo sapiens", resource="event-hierarchy") # References If you use `gget reactome` in a publication, please cite the following articles: -- Luebbert, L., & Pachter, L. (2023). Efficient querying of genomic reference databases with gget. Bioinformatics. [https://doi.org/10.1093/bioinformatics/btac836](https://doi.org/10.1093/bioinformatics/btac836) - - Marc Gillespie, Bijay Jassal, Ralf Stephan, Marija Milacic, Karen Rothfels, Andrea Senff-Ribeiro, Johannes Griss, Cristoffer Sevilla, Lisa Matthews, Chuqiao Gong, Chuan Deng, Thawfeek Varusai, Eliot Ragueneau, Yusra Haider, Bruce May, Veronica Shamovsky, Joel Weiser, Timothy Brunson, Nasim Sanati, Liam Beckman, Xiang Shao, Antonio Fabregat, Konstantinos Sidiropoulos, Julieth Murillo, Guilherme Viteri, Justin Cook, Solomon Shorser, Gary Bader, Emek Demir, Chris Sander, Robin Haw, Guanming Wu, Lincoln Stein, Henning Hermjakob, Peter D'Eustachio (2022). The reactome pathway knowledgebase 2022. Nucleic Acids Research, Volume 50, Issue D1, 7 January 2022, Pages D687–D692, [https://doi.org/10.1093/nar/gkab1028](https://doi.org/10.1093/nar/gkab1028) + +- Luebbert, L., & Pachter, L. (2023). Efficient querying of genomic reference databases with gget. Bioinformatics. [https://doi.org/10.1093/bioinformatics/btac836](https://doi.org/10.1093/bioinformatics/btac836) diff --git a/docs/src/en/updates.md b/docs/src/en/updates.md index b1d5dda5b..37a135d20 100644 --- a/docs/src/en/updates.md +++ b/docs/src/en/updates.md @@ -5,7 +5,7 @@ #### *gget* officially became part of [*scverse*](https://scverse.org/) on June 9, 2026. 🥳🥳🥳 **Version ≥ 0.30.9** (XXX XX, 2026): -- [`gget reactome`](reactome.md): **New module** to query the [Reactome](https://reactome.org/) pathway knowledgebase via its ContentService REST API. Query types are selected with the `resource` argument: `pathways` (the pathways an identifier such as a UniProt accession participates in), `search` (full-text search), `entity` (details for a Reactome stable ID), `interactors` (molecular interactors of an identifier), `orthology` (project a stable ID to its ortholog in another species), and `event-hierarchy` (the full pathway/reaction hierarchy for a species). Returned data frames carry the Reactome release version in `.attrs["reactome_release"]` for reproducibility. Resolves [issue 114](https://github.com/scverse/gget/issues/114). +- [`gget reactome`](reactome.md): **New module** to query the [Reactome](https://reactome.org/) pathway knowledgebase via its ContentService REST API. Query types are selected with the `resource` argument: `pathways` (the pathways an identifier such as a UniProt accession participates in), `search` (full-text search), `entity` (details for a Reactome stable ID), `interactors` (molecular interactors of an identifier), `orthology` (project a stable ID to its ortholog in another species), and `event-hierarchy` (the full pathway/reaction hierarchy for a species). Returned data frames carry the Reactome release version in `.attrs["reactome_release"]` for reproducibility. **Version ≥ 0.30.8** (Jun 28, 2026): - [`gget g2p`](g2p.md): Either `gene` or `--uniprot_id` is now sufficient — whichever is missing is resolved via UniProt and cached. Gene→UniProt picks the canonical reviewed human Swiss-Prot entry; the resolution and its limitations are logged. The canonical pair is **always** prepended to the result as `gene_name` / `uniprot_id` columns (and stored on `df.attrs`), so the output schema is invariant regardless of input mode. Existing call sites continue to work. diff --git a/docs/src/es/reactome.md b/docs/src/es/reactome.md new file mode 100644 index 000000000..c26837881 --- /dev/null +++ b/docs/src/es/reactome.md @@ -0,0 +1,158 @@ +[ Ver el codigo fuente de la pagina en GitHub ](https://github.com/scverse/gget/blob/main/docs/src/es/reactome.md) + +> Parámetros de Python són iguales a los parámetros largos (`--parámetro`) de Terminal, si no especificado de otra manera. Banderas son parámetros de verdadero o falso (True/False) en Python. El manuál para cualquier modulo de gget se puede llamar desde la Terminal con la bandera `-h` `--help`. +# gget reactome 🔬 +Consulte la base de conocimientos de rutas [Reactome](https://reactome.org/) usando su [API REST ContentService](https://reactome.org/dev/content-service). +Regresa: JSON/CSV (línea de comandos) o data frame (Python). + +`gget reactome` admite varios tipos de consulta, seleccionados con el argumento `resource`: +- `pathways` (por defecto): regresa las rutas de Reactome en las que participa un identificador dado (p. ej. un número de acceso UniProt). +- `search`: búsqueda de texto completo en la base de conocimientos de Reactome (rutas, reacciones, entidades físicas, ...). +- `entity`: regresa los detalles de un ID estable de Reactome (p. ej. `R-HSA-6804754`). +- `interactors`: regresa los interactores moleculares de un identificador (interactores estáticos de IntAct). +- `orthology`: proyecta un ID estable de Reactome a su ortólogo en otra especie (requiere `--species`). +- `event-hierarchy`: regresa la jerarquía completa de rutas/reacciones de una especie. + +**Parámetro posicional** +`query` +Identificador, término de búsqueda o especie a consultar. Su significado depende de `resource`: +- `resource="pathways"`: un identificador, p. ej. el número de acceso UniProt `P04637`. +- `resource="search"`: un término de búsqueda de texto libre, p. ej. `TP53`. +- `resource="entity"`: un ID estable de Reactome, p. ej. `R-HSA-6804754`. +- `resource="interactors"`: un número de acceso de molécula, p. ej. UniProt `P04637`. +- `resource="orthology"`: un ID estable de Reactome a proyectar, p. ej. `R-HSA-6804754`. +- `resource="event-hierarchy"`: un nombre de especie o ID de taxonomía de NCBI, p. ej. `Homo sapiens`. + +**Parámetros opcionales** +`-r` `--resource` +Tipo de consulta a realizar. Opciones: `pathways` (por defecto), `search`, `entity`, `interactors`, `orthology`, `event-hierarchy`. + +`-s` `--source` +Recurso/base de datos del identificador para `resource="pathways"`, p. ej. `UniProt` (por defecto), `Ensembl`, `ChEBI`, `NCBI`. + +`-sp` `--species` +Especie, como nombre (p. ej. `Homo sapiens`) o ID de taxonomía de NCBI (p. ej. `9606`). Filtra `resource="pathways"` y `resource="search"`; es el **objetivo requerido** para `resource="orthology"`. Por defecto: None. + +`-t` `--types` +Restringe los resultados de `resource="search"` a uno o más tipos de entrada, p. ej. `Pathway`, `Reaction`, `Protein`. Por defecto: None (todos los tipos). + +`-o` `--out` +Ruta al archivo en el que se guardarán los resultados, p. ej. ruta/al/directorio/resultados.json. Por defecto: salida estándar (STDOUT). + +**Banderas** +`-csv` `--csv` +Solo para la Terminal. Regresa los resultados en formato CSV, en lugar de formato JSON. +Para Python: usa `json=True` para regresar los resultados en formato JSON. + +`-q` `--quiet` +Solo para la Terminal. Impide que la información de progreso se muestre durante la corrida. +Para Python: usa `verbose=False` para impedir que la información de progreso se muestre durante la corrida. + + +### Ejemplos + +**Obtenga las rutas de Reactome en las que participa una proteína** + +```bash +gget reactome P04637 --species "Homo sapiens" +``` + +```python +import gget +gget.reactome("P04637", species="Homo sapiens") +``` + +→ Regresa las rutas de Reactome en las que participa la proteína UniProt P04637 (TP53 humano). + +| stable_id | name | species | schema_class | in_disease | +|---------------|------------------------------------------------------|--------------|--------------|------------| +| R-HSA-111448 | Activation of NOXA and translocation to mitochondria | Homo sapiens | Pathway | False | +| R-HSA-139915 | Activation of PUMA and translocation to mitochondria | Homo sapiens | Pathway | False | +| ... | ... | ... | ... | ... | + +

+ +**Busque en la base de conocimientos de Reactome** + +```bash +gget reactome TP53 -r search -t Pathway -sp "Homo sapiens" +``` +```python +import gget +gget.reactome("TP53", resource="search", types="Pathway", species="Homo sapiens") +``` + +→ Regresa las rutas de Reactome que coinciden con el término de búsqueda "TP53". + +| stable_id | name | type | species | reactome_id | +|---------------|-------------------------------|---------|--------------|---------------| +| R-HSA-6804754 | Regulation of TP53 Expression | Pathway | Homo sapiens | R-HSA-6804754 | +| R-HSA-5633007 | Regulation of TP53 Activity | Pathway | Homo sapiens | R-HSA-5633007 | +| ... | ... | ... | ... | ... | + +

+ +**Obtenga los detalles de una entrada de Reactome por su ID estable** + +```bash +gget reactome R-HSA-6804754 -r entity +``` +```python +import gget +gget.reactome("R-HSA-6804754", resource="entity") +``` + +→ Regresa los detalles de la entrada de Reactome R-HSA-6804754. + +| stable_id | name | schema_class | species | in_disease | summation | +|---------------|-------------------------------|--------------|--------------|------------|------------------| +| R-HSA-6804754 | Regulation of TP53 Expression | Pathway | Homo sapiens | False | Transcription... | + +

+ +**Obtenga los interactores moleculares de una proteína** + +```bash +gget reactome P04637 -r interactors +``` +```python +gget.reactome("P04637", resource="interactors") +``` + +→ Regresa los interactores de IntAct de P04637 (columnas: `interactor_acc`, `interactor_name`, `score`, `evidences`), p. ej. MDM2, con una puntuación de confianza de la interacción. + +

+ +**Proyecte una ruta a otra especie (ortología)** + +```bash +gget reactome R-HSA-6804754 -r orthology -sp "Mus musculus" +``` +```python +gget.reactome("R-HSA-6804754", resource="orthology", species="Mus musculus") +``` + +→ Regresa el ortólogo en ratón de la ruta humana (`R-MMU-6804754`, "Regulation of TP53 Expression", Mus musculus). + +

+ +**Obtenga la jerarquía completa de eventos (rutas/reacciones) de una especie** + +```bash +gget reactome "Homo sapiens" -r event-hierarchy +``` +```python +gget.reactome("Homo sapiens", resource="event-hierarchy") +``` + +→ Regresa toda la jerarquía de eventos humana aplanada a una fila por evento (columnas: `stable_id`, `name`, `type`, `species`, `parent_id`, `level`). Es una tabla grande — use `parent_id`/`level` para navegar el árbol. + +> El DataFrame regresado guarda la versión de lanzamiento de Reactome en `.attrs["reactome_release"]` (p. ej. `97`) para reproducibilidad. + + +# Citar +Si utiliza `gget reactome` en una publicación, favor de citar los siguientes artículos: + +- Marc Gillespie, Bijay Jassal, Ralf Stephan, Marija Milacic, Karen Rothfels, Andrea Senff-Ribeiro, Johannes Griss, Cristoffer Sevilla, Lisa Matthews, Chuqiao Gong, Chuan Deng, Thawfeek Varusai, Eliot Ragueneau, Yusra Haider, Bruce May, Veronica Shamovsky, Joel Weiser, Timothy Brunson, Nasim Sanati, Liam Beckman, Xiang Shao, Antonio Fabregat, Konstantinos Sidiropoulos, Julieth Murillo, Guilherme Viteri, Justin Cook, Solomon Shorser, Gary Bader, Emek Demir, Chris Sander, Robin Haw, Guanming Wu, Lincoln Stein, Henning Hermjakob, Peter D'Eustachio (2022). The reactome pathway knowledgebase 2022. Nucleic Acids Research, Volume 50, Issue D1, 7 January 2022, Pages D687–D692, [https://doi.org/10.1093/nar/gkab1028](https://doi.org/10.1093/nar/gkab1028) + +- Luebbert, L., & Pachter, L. (2023). Efficient querying of genomic reference databases with gget. Bioinformatics. [https://doi.org/10.1093/bioinformatics/btac836](https://doi.org/10.1093/bioinformatics/btac836)