Skip to content

Commit

Permalink
chore: better handle CancelledError from static analysis
Browse files Browse the repository at this point in the history
We have seen stacktraces of static analysis failing with
`asyncio.exceptions.CancelledError: Cancelled by cancel scope 7f7514b8ffa0`

I do believe that the initial cause is one of the requests failing, and that cancels every other task, but
there's a still possible scenario in which the outer future (`asyncio.gather itself`) gets cancelled.

Assuming that it happens and that we do get the same exception bubble up, these changes would
let the program exit (more) gracefully.

Maybe there are other exceptions there that we should also look for?
  • Loading branch information
giovanni-guidini committed Oct 4, 2023
1 parent 1da13a0 commit 0fed329
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 1 deletion.
9 changes: 8 additions & 1 deletion codecov_cli/services/staticanalysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,14 @@ async def run_analysis_entrypoint(
for el in files_that_need_upload:
all_tasks.append(send_single_upload_put(client, all_data, el))
bar.update(1, all_data[el["filepath"]])
resps = await asyncio.gather(*all_tasks)
try:
resps = await asyncio.gather(*all_tasks)
except asyncio.CancelledError:
message = (
"Unknown error cancelled the upload tasks.\n"
+ f"Uploaded {len(uploaded_files)}/{len(files_that_need_upload)} files successfully."
)
raise click.ClickException(message)
for resp in resps:
if resp["succeeded"]:
uploaded_files.append(resp["filepath"])
Expand Down
71 changes: 71 additions & 0 deletions tests/services/static_analysis/test_static_analysis_service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from asyncio import CancelledError
from pathlib import Path
from unittest.mock import MagicMock

Expand Down Expand Up @@ -148,6 +149,76 @@ async def side_effect(*args, **kwargs):
"raw_upload_location": "http://storage-url",
}

@pytest.mark.asyncio
async def test_static_analysis_service_CancelledError(self, mocker):
mock_file_finder = mocker.patch(
"codecov_cli.services.staticanalysis.select_file_finder"
)
mock_send_upload_put = mocker.patch(
"codecov_cli.services.staticanalysis.send_single_upload_put"
)

async def side_effect(client, all_data, el):
if el["filepath"] == "samples/inputs/sample_001.py":
return {
"status_code": 204,
"filepath": el["filepath"],
"succeeded": True,
}
raise CancelledError("Pretending something cancelled this task")

mock_send_upload_put.side_effect = side_effect

files_found = map(
lambda filename: FileAnalysisRequest(str(filename), Path(filename)),
[
"samples/inputs/sample_001.py",
"samples/inputs/sample_002.py",
],
)
mock_file_finder.return_value.find_files = MagicMock(return_value=files_found)
with responses.RequestsMock() as rsps:
rsps.add(
responses.POST,
"https://api.codecov.io/staticanalysis/analyses",
json={
"external_id": "externalid",
"filepaths": [
{
"state": "created",
"filepath": "samples/inputs/sample_001.py",
"raw_upload_location": "http://storage-url-001",
},
{
"state": "created",
"filepath": "samples/inputs/sample_002.py",
"raw_upload_location": "http://storage-url-002",
},
],
},
status=200,
match=[
matchers.header_matcher({"Authorization": "Repotoken STATIC_TOKEN"})
],
)

with pytest.raises(click.ClickException) as exp:
await run_analysis_entrypoint(
config={},
folder=".",
numberprocesses=1,
pattern="*.py",
token="STATIC_TOKEN",
commit="COMMIT",
should_force=False,
folders_to_exclude=[],
enterprise_url=None,
)
assert "Unknown error cancelled the upload tasks." in str(exp.value)
mock_file_finder.assert_called_with({})
mock_file_finder.return_value.find_files.assert_called()
assert mock_send_upload_put.call_count == 2

@pytest.mark.asyncio
async def test_send_single_upload_put_success(self, mocker):
mock_client = MagicMock()
Expand Down

0 comments on commit 0fed329

Please sign in to comment.