Skip to content

Commit 3b787b3

Browse files
committed
Bug 1997236 - Don't try to re-parse JSON in util.taskcluster.get_artifact
The previous code was only checking if the returned response from the taskcluster API was a dict to verify if it had been parsed or not. That works well until the JSON file we're getting contains something like a list in which case we skip the first condition, end up with a parsed response in `if path.endswith(".json")` and boom `AttributeError: 'list' object has no attribute 'json'`. Since we already rely on the response type being `requests.Request`, I changed the logic to be: - If we receive a dict, it might be either the parsed artifact content, or taskcluster giving us `{"response": requests.Response}` because it was unable to parse it. - In the case where it's taskcluster's (detected by checking if "response" is in the dict and if it has the right type), peel it off, response is now the `Response` object and we can read from it. - At this point we have 2 choices again, either response is a `request.Response` that we have to read, or anything else. If it is anything else then it's already been parsed by taskcluster so we just return it as is. - Continue with the previous logic, try to parse the file based on the extension. I left the JSON case in there even though we should not be hitting it because it will raise on invalid JSON, which was the case previously too. Taskcluster masks JSON failures by returning the response object instead.
1 parent 773f5d2 commit 3b787b3

File tree

2 files changed

+15
-3
lines changed

2 files changed

+15
-3
lines changed

src/taskgraph/util/taskcluster.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ def _handle_artifact(
7979
response["response"], requests.Response
8080
):
8181
response = response["response"]
82-
else:
83-
# If we already a dict (parsed JSON), return it directly.
84-
return response
82+
83+
if not isinstance(response, requests.Response):
84+
# At this point, if we don't have a response object, it's already parsed, return it
85+
return response
8586

8687
# We have a response object, load the content based on the path extension.
8788
if path.endswith(".json"):

test/test_util_taskcluster.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ def test_get_artifact(responses, root_url):
124124
result = tc.get_artifact(tid, "artifact.json")
125125
assert result == {"foo": "bar"}
126126

127+
# Test JSON artifact that isn't a dict (bug 1997236)
128+
responses.get("http://foo.bar/artifact.json", json=[1, 2, 3])
129+
responses.get(
130+
f"{root_url}/api/queue/v1/task/{tid}/artifacts/artifact.json",
131+
body=b'{"type": "s3", "url": "http://foo.bar/artifact.json"}',
132+
status=303,
133+
headers={"Location": "http://foo.bar/artifact.json"},
134+
)
135+
result = tc.get_artifact(tid, "artifact.json")
136+
assert result == [1, 2, 3]
137+
127138
# Test YAML artifact
128139
expected_result = {"foo": b"\xe2\x81\x83".decode()}
129140
responses.get(

0 commit comments

Comments
 (0)