Skip to content

Commit 889bc84

Browse files
Handle missing repodata gracefully
1 parent 187a794 commit 889bc84

2 files changed

Lines changed: 68 additions & 2 deletions

File tree

vinca/test_utils.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import json
2+
from unittest.mock import Mock, patch
3+
4+
from vinca.utils import get_repodata
5+
6+
EMPTY_REPODATA = {"packages": {}, "packages.conda": {}}
7+
8+
9+
def test_get_repodata_returns_empty_for_missing_local_repodata(tmp_path):
10+
assert get_repodata(str(tmp_path), "osx-arm64") == EMPTY_REPODATA
11+
12+
13+
def test_get_repodata_returns_empty_for_missing_remote_repodata(tmp_path, monkeypatch):
14+
monkeypatch.chdir(tmp_path)
15+
response = Mock(status_code=404, content=b"Not Found")
16+
17+
with patch("vinca.utils.requests.get", return_value=response):
18+
assert (
19+
get_repodata("https://example.com/channel", "osx-arm64") == EMPTY_REPODATA
20+
)
21+
22+
23+
def test_get_repodata_returns_empty_for_non_json_remote_repodata(tmp_path, monkeypatch):
24+
monkeypatch.chdir(tmp_path)
25+
response = Mock(status_code=200, content=b"Not Found")
26+
response.raise_for_status.return_value = None
27+
28+
with patch("vinca.utils.requests.get", return_value=response):
29+
assert (
30+
get_repodata("https://example.com/channel", "osx-arm64") == EMPTY_REPODATA
31+
)
32+
33+
34+
def test_get_repodata_ignores_invalid_cached_repodata(tmp_path, monkeypatch):
35+
monkeypatch.chdir(tmp_path)
36+
url = "https://example.com/channel/osx-arm64/repodata.json"
37+
cache_name = "vinca_d7e1ca2423.json"
38+
(tmp_path / cache_name).write_text("Not Found")
39+
repodata = {"packages": {"pkg.tar.bz2": {"name": "pkg"}}, "packages.conda": {}}
40+
response = Mock(status_code=200, content=json.dumps(repodata).encode("utf-8"))
41+
response.raise_for_status.return_value = None
42+
43+
with patch("vinca.utils.requests.get", return_value=response):
44+
assert get_repodata(url) == repodata

vinca/utils.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ def get_repodata(url_or_path, platform=None):
4444
url_or_path += f"{platform}/repodata.json"
4545

4646
if "://" not in url_or_path:
47+
if not os.path.exists(url_or_path):
48+
print(f"No repodata found at {url_or_path}, assuming no existing packages")
49+
return {"packages": {}, "packages.conda": {}}
4750
with open(url_or_path) as fi:
4851
return json.load(fi)
4952
print("Downloading repodata from ", url_or_path)
@@ -58,13 +61,32 @@ def get_repodata(url_or_path, platform=None):
5861
max_age = 100_000 # seconds == 27 hours
5962
if age < max_age:
6063
with open(fn) as fi:
61-
return json.load(fi)
64+
try:
65+
return json.load(fi)
66+
except json.JSONDecodeError:
67+
print(f"Ignoring invalid cached repodata at {fn}")
68+
os.remove(fn)
6269

6370
repodata = requests.get(url_or_path)
71+
if repodata.status_code == 404:
72+
print(f"No repodata found at {url_or_path}, assuming no existing packages")
73+
return {"packages": {}, "packages.conda": {}}
74+
repodata.raise_for_status()
6475
content = repodata.content
76+
if not content.strip():
77+
print(f"No repodata found at {url_or_path}, assuming no existing packages")
78+
return {"packages": {}, "packages.conda": {}}
79+
try:
80+
parsed_repodata = json.loads(content)
81+
except json.JSONDecodeError:
82+
print(
83+
f"No valid repodata found at {url_or_path}, assuming no existing packages"
84+
)
85+
return {"packages": {}, "packages.conda": {}}
86+
6587
with open(fn, "w") as fcache:
6688
fcache.write(content.decode("utf-8"))
67-
return json.loads(content)
89+
return parsed_repodata
6890

6991

7092
def ensure_name_is_without_distro_prefix_and_with_underscores(name, vinca_conf):

0 commit comments

Comments
 (0)