Skip to content

Commit 593f106

Browse files
authored
utils.studio: refactor to return json response (#9277)
1 parent 35bb261 commit 593f106

File tree

2 files changed

+31
-26
lines changed

2 files changed

+31
-26
lines changed

dvc/utils/studio.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import logging
2-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
2+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
33
from urllib.parse import urljoin
44

55
import requests
6-
from funcy import compact
6+
from funcy import compact, ignore
77
from requests.adapters import HTTPAdapter
88

99
if TYPE_CHECKING:
@@ -29,9 +29,9 @@ def post(
2929
logger.trace("Sending %s to %s", data, endpoint) # type: ignore[attr-defined]
3030

3131
headers = {"Authorization": f"token {token}"}
32-
resp = session.post(endpoint, json=data, headers=headers, timeout=timeout)
33-
resp.raise_for_status()
34-
return resp
32+
r = session.post(endpoint, json=data, headers=headers, timeout=timeout)
33+
r.raise_for_status()
34+
return r
3535

3636

3737
def notify_refs(
@@ -40,13 +40,13 @@ def notify_refs(
4040
*,
4141
studio_url: Optional[str] = STUDIO_URL,
4242
**refs: List[str],
43-
) -> None:
43+
) -> Dict[str, Any]:
4444
extra_keys = refs.keys() - {"pushed", "removed"}
4545
assert not extra_keys, f"got extra args: {extra_keys}"
4646

4747
refs = compact(refs)
4848
if not refs:
49-
return
49+
return {}
5050

5151
logger.debug(
5252
"notifying Studio%s about updated experiments",
@@ -55,22 +55,26 @@ def notify_refs(
5555
data = {"repo_url": repo_url, "client": "dvc", "refs": refs}
5656

5757
try:
58-
post("/webhook/dvc", token, data, url=studio_url)
58+
r = post("/webhook/dvc", token, data, url=studio_url)
5959
except requests.RequestException as e:
6060
logger.debug("", exc_info=True)
6161

6262
msg = str(e)
63-
if (r := e.response) is not None:
64-
status = r.status_code
65-
# try to parse json response for more detailed error message
66-
try:
67-
d = r.json()
68-
logger.trace( # type: ignore[attr-defined]
69-
"received response: %s (status=%r)", d, status
70-
)
71-
except requests.JSONDecodeError:
72-
pass
73-
else:
74-
if detail := d.get("detail"):
75-
msg = f"{detail} ({status=})"
63+
if e.response is None:
64+
logger.warning("failed to notify Studio: %s", msg.lower())
65+
return {}
66+
67+
r = cast("Response", e.response)
68+
d = ignore(Exception, default={})(r.json)()
69+
status = r.status_code
70+
if detail := d.get("detail"):
71+
msg = f"{detail} ({status=})"
7672
logger.warning("failed to notify Studio: %s", msg.lower())
73+
else:
74+
d = r.json()
75+
76+
if d:
77+
logger.trace( # type: ignore[attr-defined]
78+
"received response: %s (status=%r)", d, r.status_code
79+
)
80+
return d

tests/unit/utils/test_studio.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77

88

99
@pytest.mark.parametrize(
10-
"status_code",
10+
"status_code, side_effect",
1111
[
12-
200, # success
13-
401, # should not fail on client errors
14-
500, # should not fail even on server errors
12+
(200, {}), # success
13+
(401, {"detail": "unauthorized"}), # should not fail on client errors
14+
(500, ValueError), # should not fail even on server errors
1515
],
1616
)
17-
def test_notify_refs(mocker, status_code):
17+
def test_notify_refs(mocker, status_code, side_effect):
1818
response = Response()
1919
response.status_code = status_code
20+
mocker.patch.object(response, "json", side_effect=[side_effect])
2021

2122
mock_post = mocker.patch("requests.Session.post", return_value=response)
2223

0 commit comments

Comments
 (0)