Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- Checks for MIBiG and Rhea IDs (implemented as warnings in mite_validation)
- Timeout for UniProt, NCBI connection
- Timeout for UniProt using requests
- Timeout for NCBI using concurrent.futures (Biopython Entrez lacks timeout, requests blocked by NCBI servers)

### Fixed

Expand Down
38 changes: 23 additions & 15 deletions mite_data/modules/fasta_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@
this CC0 or use of the Work.
"""

import concurrent.futures
import json
import logging
from pathlib import Path
from typing import Self
from typing import Any, Self

import requests
from Bio import Entrez
Expand Down Expand Up @@ -201,22 +202,29 @@ def download_ncbi(self: Self, mite_acc: str, genpept_acc: str) -> tuple:

Returns:
Tuple of path and data for storage

Notes:
concurrent.futures needed to implement timeout since Biopythons Entrez lacks it and requests is blocked by NCBI
"""
url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"
params = {
"db": "protein",
"id": genpept_acc,
"rettype": "fasta",
"retmode": "text",
}
try:
response = requests.get(url, params, timeout=self.timeout)
except requests.exceptions.ConnectTimeout as e:
raise RuntimeError("Warning: could not connect to NCBI: Timeout") from e

if response is None:
raise RuntimeError(f"{mite_acc}: NCBI download failed on ID {genpept_acc}")
lines = response.text.strip().splitlines()
def fetch_ncbi(acc: str) -> Any:
"""Fetch data from NCBI"""
handle = Entrez.efetch(
db="protein", id=genpept_acc, rettype="fasta", retmode="text"
)
data = handle.read().strip()
handle.close()
return data

with concurrent.futures.ThreadPoolExecutor() as ex:
future = ex.submit(fetch_ncbi, genpept_acc)

try:
fasta_data = future.result(timeout=self.timeout)
except concurrent.futures.TimeoutError as e:
raise RuntimeError("Warning: could not connect to NCBI: Timeout") from e

lines = fasta_data.splitlines()

if not lines or len(lines) == 1:
raise ValueError(
Expand Down
14 changes: 7 additions & 7 deletions tests/test_fasta_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ def test_download_ncbi(fasta_manager):
assert content


def test_download_ncbi_timeout(fasta_manager):
mngr = copy.deepcopy(fasta_manager)
mngr.timeout = 0.000001
with pytest.raises(RuntimeError):
mngr.download_ncbi(mite_acc="MITE9999999", genpept_acc="AAD28496.1")


def test_download_uniprot(fasta_manager):
path, content = fasta_manager.download_uniprot(
mite_acc="MITE9999999", uniprot_acc="Q9X2V9"
Expand All @@ -57,6 +50,13 @@ def test_download_uniprot(fasta_manager):
assert content


def test_download_ncbi_timeout(fasta_manager):
mngr = copy.deepcopy(fasta_manager)
mngr.timeout = 0.000001
with pytest.raises(RuntimeError):
mngr.download_ncbi(mite_acc="MITE9999999", genpept_acc="AAD28496.1")


def test_download_uniprot_timeout(fasta_manager):
mngr = copy.deepcopy(fasta_manager)
mngr.timeout = 0.000001
Expand Down