From 9365322b637413acb1c45616d14e0ca3f0d1f155 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Sat, 3 Oct 2026 10:36:44 +0530 Subject: [PATCH] fix: close download responses when opening the destination fails --- dropbox/dropbox_client.py | 4 ++-- test/unit/test_dropbox_unit.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/dropbox/dropbox_client.py b/dropbox/dropbox_client.py index 8d7cf1f..180727f 100644 --- a/dropbox/dropbox_client.py +++ b/dropbox/dropbox_client.py @@ -775,8 +775,8 @@ def _save_body_to_file(self, download_path, http_resp, chunksize=2**16): :type http_resp: :class:`requests.models.Response` :rtype: None """ - with open(download_path, "wb") as f: - with contextlib.closing(http_resp): + with contextlib.closing(http_resp): + with open(download_path, "wb") as f: for c in http_resp.iter_content(chunksize): f.write(c) diff --git a/test/unit/test_dropbox_unit.py b/test/unit/test_dropbox_unit.py index 0a8bb30..554ff82 100644 --- a/test/unit/test_dropbox_unit.py +++ b/test/unit/test_dropbox_unit.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import inspect +import io import json import pickle from datetime import datetime, timedelta @@ -8,6 +9,7 @@ import pytest import requests +from urllib3.response import HTTPResponse # Tests OAuth Flow from dropbox import DropboxOAuth2Flow, session, Dropbox, create_session @@ -305,6 +307,21 @@ def test_NoRedirect_whole_flow(self, auth_flow_offline_with_scopes): class TestClient: + @pytest.mark.parametrize("missing_parent", [False, True]) + def test_download_closes_response(self, session_instance, tmp_path, missing_parent): + response = requests.Response() + response.raw = HTTPResponse(body=io.BytesIO(b"downloaded content"), preload_content=False) + dbx = Dropbox(ACCESS_TOKEN, session=session_instance) + destination = tmp_path / "download.bin" + if missing_parent: + destination = tmp_path / "missing" / "download.bin" + with pytest.raises(FileNotFoundError): + dbx._save_body_to_file(destination, response) + else: + dbx._save_body_to_file(destination, response) + assert destination.read_bytes() == b"downloaded content" + assert response.raw.closed + @pytest.fixture(scope="function") def session_instance(self, mocker): session_obj = create_session()