Skip to content

Commit 6ad278f

Browse files
authored
Merge pull request #2794 from pared/2510
remote: http: raise exception when response with error status code
2 parents 510ff8a + 03c2719 commit 6ad278f

File tree

5 files changed

+30
-16
lines changed

5 files changed

+30
-16
lines changed

dvc/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,8 @@ def __init__(self, path, external_repo_path, external_repo_url):
348348
relpath(path, external_repo_path), external_repo_url
349349
)
350350
)
351+
352+
353+
class HTTPError(DvcException):
354+
def __init__(self, code, reason):
355+
super(HTTPError, self).__init__("'{} {}'".format(code, reason))

dvc/remote/http.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from dvc.config import Config
99
from dvc.config import ConfigError
10-
from dvc.exceptions import DvcException
10+
from dvc.exceptions import DvcException, HTTPError
1111
from dvc.progress import Tqdm
1212
from dvc.remote.base import RemoteBASE
1313
from dvc.scheme import Schemes
@@ -37,30 +37,26 @@ def __init__(self, repo, config):
3737
)
3838

3939
def _download(self, from_info, to_file, name=None, no_progress_bar=False):
40-
request = self._request("GET", from_info.url, stream=True)
40+
response = self._request("GET", from_info.url, stream=True)
41+
if response.status_code != 200:
42+
raise HTTPError(response.status_code, response.reason)
4143
with Tqdm(
42-
total=None if no_progress_bar else self._content_length(from_info),
44+
total=None if no_progress_bar else self._content_length(response),
4345
leave=False,
4446
bytes=True,
4547
desc=from_info.url if name is None else name,
4648
disable=no_progress_bar,
4749
) as pbar:
4850
with open(to_file, "wb") as fd:
49-
for chunk in request.iter_content(chunk_size=self.CHUNK_SIZE):
51+
for chunk in response.iter_content(chunk_size=self.CHUNK_SIZE):
5052
fd.write(chunk)
51-
fd.flush()
5253
pbar.update(len(chunk))
5354

5455
def exists(self, path_info):
5556
return bool(self._request("HEAD", path_info.url))
5657

57-
def _content_length(self, url_or_request):
58-
headers = getattr(
59-
url_or_request,
60-
"headers",
61-
self._request("HEAD", url_or_request).headers,
62-
)
63-
res = headers.get("Content-Length")
58+
def _content_length(self, response):
59+
res = response.headers.get("Content-Length")
6460
return int(res) if res else None
6561

6662
def get_file_checksum(self, path_info):

tests/func/test_repro.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from tests.func.test_data_cloud import get_ssh_url
4444
from tests.func.test_data_cloud import TEST_AWS_REPO_BUCKET
4545
from tests.func.test_data_cloud import TEST_GCP_REPO_BUCKET
46-
from tests.utils.httpd import StaticFileServer
46+
from tests.utils.httpd import StaticFileServer, ContentMD5Handler
4747

4848

4949
class TestRepro(TestDvc):
@@ -1176,7 +1176,7 @@ def test(self):
11761176

11771177
self.dvc.remove("imported_file.dvc")
11781178

1179-
with StaticFileServer(handler="Content-MD5") as httpd:
1179+
with StaticFileServer(handler_class=ContentMD5Handler) as httpd:
11801180
import_url = urljoin(self.get_remote(httpd.server_port), self.FOO)
11811181
import_output = "imported_file"
11821182
import_stage = self.dvc.imp_url(import_url, import_output)

tests/unit/remote/test_http.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import pytest
22

33
from dvc.config import ConfigError
4+
from dvc.exceptions import HTTPError
5+
from dvc.path_info import URLInfo
46
from dvc.remote.http import RemoteHTTP
7+
from tests.utils.httpd import StaticFileServer
58

69

710
def test_no_traverse_compatibility(dvc_repo):
@@ -13,3 +16,14 @@ def test_no_traverse_compatibility(dvc_repo):
1316

1417
with pytest.raises(ConfigError):
1518
RemoteHTTP(dvc_repo, config)
19+
20+
21+
def test_download_fails_on_error_code(dvc_repo):
22+
with StaticFileServer() as httpd:
23+
url = "http://localhost:{}/".format(httpd.server_port)
24+
config = {"url": url}
25+
26+
remote = RemoteHTTP(dvc_repo, config)
27+
28+
with pytest.raises(HTTPError):
29+
remote._download(URLInfo(url) / "missing.txt", "missing.txt")

tests/utils/httpd.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ class ContentMD5Handler(TestRequestHandler):
4545
class StaticFileServer:
4646
_lock = threading.Lock()
4747

48-
def __init__(self, handler="etag"):
48+
def __init__(self, handler_class=ETagHandler):
4949
self._lock.acquire()
50-
handler_class = ETagHandler if handler == "etag" else ContentMD5Handler
5150
self._httpd = HTTPServer(("localhost", 0), handler_class)
5251
self._thread = None
5352

0 commit comments

Comments
 (0)