Skip to content

Commit 8c94171

Browse files
committed
Cleanup IO utils
1 parent 4f38d6c commit 8c94171

4 files changed

Lines changed: 28 additions & 37 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["uv_build>=0.6.6,<0.7"]
2+
requires = ["uv_build>=0.6.6,<1.0"]
33
build-backend = "uv_build"
44

55
[project]
@@ -69,7 +69,7 @@ dependencies = [
6969
"humanize",
7070
"tabulate",
7171
"cachier",
72-
"pystow>=0.7.0",
72+
"pystow>=0.7.5",
7373
"bioversions>=0.8.101",
7474
"bioregistry>=0.12.30",
7575
"bioontologies>=0.7.2",

src/pyobo/getters.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,14 @@ def iter_helper_helper(
367367
logger.warning("[%s] HTTP %s: unable to download %s", prefix, e.getcode(), e.geturl())
368368
if strict and not bioregistry.is_deprecated(prefix):
369369
raise
370-
except (urllib.error.URLError, requests.exceptions.ConnectTimeout) as e:
370+
except urllib.error.URLError as e:
371371
logger.warning("[%s] unable to download - %s", prefix, e.reason)
372372
if strict and not bioregistry.is_deprecated(prefix):
373373
raise
374+
except requests.exceptions.ConnectTimeout as e:
375+
logger.warning("[%s] unable to download - %s", prefix, e)
376+
if strict and not bioregistry.is_deprecated(prefix):
377+
raise
374378
except ParseError as e:
375379
if not e.node:
376380
logger.warning("[%s] %s", prefix, e)

src/pyobo/sources/uniprot/uniprot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55
from typing import cast
66

7+
from pystow.utils import safe_open_reader
78
from tqdm.auto import tqdm
89

910
from pyobo import Obo, Reference
@@ -22,7 +23,6 @@
2223
participates_in,
2324
)
2425
from pyobo.struct.typedef import gene_product_of, located_in, molecularly_interacts_with
25-
from pyobo.utils.io import open_reader
2626

2727
PREFIX = "uniprot"
2828
BASE_URL = "https://rest.uniprot.org/uniprotkb/stream"
@@ -78,7 +78,7 @@ def iter_terms(self, force: bool = False) -> Iterable[Term]:
7878

7979
def iter_terms(version: str | None = None) -> Iterable[Term]:
8080
"""Iterate over UniProt Terms."""
81-
with open_reader(ensure(version=version)) as reader:
81+
with safe_open_reader(ensure(version=version)) as reader:
8282
_ = next(reader) # header
8383
for (
8484
uniprot_id,

src/pyobo/utils/io.py

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
from collections.abc import Generator, Iterable, Mapping
1010
from contextlib import contextmanager
1111
from pathlib import Path
12-
from typing import Literal, TextIO, TypeVar
12+
from typing import Literal, TextIO, TypeVar, cast
1313

1414
import pandas as pd
15+
import pystow.utils
16+
from pystow.utils import safe_open_reader, safe_open_writer
1517
from tqdm.auto import tqdm
1618

1719
__all__ = [
@@ -20,7 +22,6 @@
2022
"multisetdict",
2123
"open_map_tsv",
2224
"open_multimap_tsv",
23-
"open_reader",
2425
"safe_open",
2526
"safe_open_writer",
2627
"write_iterable_tsv",
@@ -34,14 +35,6 @@
3435
Y = TypeVar("Y")
3536

3637

37-
@contextmanager
38-
def open_reader(path: str | Path, sep: str = "\t"):
39-
"""Open a file and get a reader for it."""
40-
path = Path(path)
41-
with safe_open(path, read=True) as file:
42-
yield get_reader(file, sep=sep)
43-
44-
4538
def get_reader(x, sep: str = "\t"):
4639
"""Get a :func:`csv.reader` with PyOBO default settings."""
4740
return csv.reader(x, delimiter=sep, quoting=csv.QUOTE_MINIMAL)
@@ -51,18 +44,18 @@ def open_map_tsv(
5144
path: str | Path, *, use_tqdm: bool = False, has_header: bool = True
5245
) -> Mapping[str, str]:
5346
"""Load a mapping TSV file into a dictionary."""
54-
with safe_open(path, read=True) as file:
47+
rv = {}
48+
with pystow.utils.safe_open_reader(path) as reader:
5549
if has_header:
56-
next(file) # throw away header
50+
next(reader) # throw away header
5751
if use_tqdm:
58-
file = tqdm(file, desc=f"loading TSV from {path}")
59-
rv = {}
60-
for row in get_reader(file):
52+
reader = tqdm(reader, desc=f"loading TSV from {path}")
53+
for row in reader:
6154
if len(row) != 2:
6255
logger.warning("[%s] malformed row can not be put in dict: %s", path, row)
6356
continue
6457
rv[row[0]] = row[1]
65-
return rv
58+
return rv
6659

6760

6861
def open_multimap_tsv(
@@ -72,24 +65,27 @@ def open_multimap_tsv(
7265
has_header: bool = True,
7366
) -> Mapping[str, list[str]]:
7467
"""Load a mapping TSV file that has multiple mappings for each."""
75-
return multidict(_help_multimap_tsv(path=path, use_tqdm=use_tqdm, has_header=has_header))
68+
with _help_multimap_tsv(path=path, use_tqdm=use_tqdm, has_header=has_header) as file:
69+
return multidict(file)
7670

7771

72+
@contextmanager
7873
def _help_multimap_tsv(
7974
path: str | Path,
8075
*,
8176
use_tqdm: bool = False,
8277
has_header: bool = True,
83-
) -> Iterable[tuple[str, str]]:
84-
with safe_open(path, read=True) as file:
78+
) -> Generator[Iterable[tuple[str, str]], None, None]:
79+
with safe_open_reader(path) as reader:
8580
if has_header:
8681
try:
87-
next(file) # throw away header
82+
next(reader) # throw away header
8883
except gzip.BadGzipFile as e:
8984
raise ValueError(f"could not open file {path}") from e
9085
if use_tqdm:
91-
file = tqdm(file, desc=f"loading TSV from {path}")
92-
yield from get_reader(file)
86+
yield tqdm(reader, desc=f"loading TSV from {path}")
87+
else:
88+
yield cast(Iterable[tuple[str, str]], reader)
9389

9490

9591
def multidict(pairs: Iterable[tuple[X, Y]]) -> Mapping[X, list[Y]]:
@@ -156,6 +152,7 @@ def safe_open(
156152
path: str | Path, read: bool, encoding: str | None = None
157153
) -> Generator[TextIO, None, None]:
158154
"""Safely open a file for reading or writing text."""
155+
# TODO replace me!
159156
path = Path(path).expanduser().resolve()
160157
mode: Literal["rt", "wt"] = "rt" if read else "wt"
161158
if path.suffix.endswith(".gz"):
@@ -164,13 +161,3 @@ def safe_open(
164161
else:
165162
with open(path, mode=mode) as file:
166163
yield file
167-
168-
169-
@contextlib.contextmanager
170-
def safe_open_writer(f: str | Path | TextIO, *, delimiter: str = "\t"): # type:ignore
171-
"""Open a CSV writer, wrapping :func:`csv.writer`."""
172-
if isinstance(f, str | Path):
173-
with safe_open(f, read=False) as file:
174-
yield csv.writer(file, delimiter=delimiter)
175-
else:
176-
yield csv.writer(f, delimiter=delimiter)

0 commit comments

Comments
 (0)